>  기사  >  Java  >  Java Lambda 표현식 사용 사례 분석

Java Lambda 표현식 사용 사례 분석

WBOY
WBOY앞으로
2023-04-18 16:01:03719검색

Lambada 소개

lambda 표현식은 Java 8에 추가된 새로운 기능입니다. Java의 함수형 프로그래밍 개념을 소개합니다. 그렇다면 함수형 프로그래밍이란 무엇일까요?

함수형 프로그래밍: 함수형 프로그래밍은 계산을 표현식 평가로 설명하는 수학 중심의 추상화입니다.

우리가 일반적으로 객체 지향 프로그래밍이라고 부르는 것은 명령형 프로그래밍에 속합니다 함수형 프로그래밍과 명령형 프로그래밍의 차이점은 다음과 같습니다.

  • 함수형 프로그래밍은 데이터 매핑에 관심이 있는 반면 명령형 프로그래밍은 문제를 해결하는 단계와 관련이 있습니다. 문제.

  • 함수형 프로그래밍 관계 유형(대수적 구조), 명령형 프로그래밍 관계 및 문제 해결 단계 간의 관계.

함수형 프로그래밍의 본질:

함수형 프로그래밍의 함수는 컴퓨터의 함수가 아니라 수학의 함수, 즉 독립변수의 매핑을 의미합니다. 즉, 함수의 값은 함수 매개변수의 값에만 의존하고 다른 상태에는 의존하지 않습니다.

엄격한 의미에서 함수형 프로그래밍은 변경 가능한 변수, 할당, 루프 및 기타 명령형 제어 구조를 사용하지 않고 프로그래밍하는 것을 의미합니다.

함수형 프로그래밍의 이점:

함수형 프로그래밍의 이점은 주로 불변성에서 비롯됩니다. 변경 가능한 상태가 없으면 함수는 참조적으로 투명하며 부작용이 없습니다.

위 내용은 몇 가지 기본 개념이지만 이러한 측면에 대한 접촉이 적을 수 있으므로 처음에는 함수형 프로그래밍이 드문 일이라고 느꼈습니다.

간단한 예

톡이 싸다, 코드를 보여줘!

가장 간단한 예를 먼저 들고, 가장 많이 소개되는 예일 수도 있다. ㅋ!

버튼에 모니터를 추가하세요.

추가하려면 익명의 내부 클래스를 사용하세요.

submit.addActionListener(new ActionListener() {
	@Override
	public void actionPerformed(ActionEvent e) {
		JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);
	}
});

이 방법의 단점: 템플릿 코드가 많이 사용되며 실제로 필요한 유일한 코드는 메서드 본문에 있는 코드입니다. 따라서 Java 8에 도입된 람다 표현식은 이러한 종류의 코드를 단순화할 수 있습니다(물론 한계가 있습니다. 모든 익명 내부 클래스를 사용할 수 있는 것은 아니며 이에 대해서는 나중에 언급하겠습니다.).

Lambda 표현식을 사용하여 코드 단순화

submit.addActionListener((e)->{
		JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);
});

Lambda 표현식은 데이터와 같은 동작을 전달하는 익명 메서드입니다.

설명

람다 표현식을 활용한 단순화된 코드 표현이 더욱 명확해지고, 더 이상 지루한 템플릿 코드를 작성할 필요가 없다는 것을 알 수 있습니다.

더 단순화

코드 본문의 매개변수 괄호와 중괄호도 생략 가능합니다. 중괄호는 생략 가능).

ActionListener listener = e->JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);

요약익명 내부 클래스 대신 Lambda 표현식을 사용하여 객체를 생성하는 경우 Lambda 표현식의 코드 블록이 추상 메서드를 구현하는 메서드 본문을 대체하며 Lambda는 익명 메서드와 동일합니다.

람다 표현식의 구성 요소

람다 표현식은

  • 공식 매개변수 목록의 세 부분으로 구성됩니다. 형식 매개변수 목록에서는 형식 매개변수 유형을 생략할 수 있습니다. 매개변수 목록에 매개변수가 하나만 있는 경우 매개변수 목록에서 괄호를 생략할 수 있습니다.

  • 화살표(->). 영어 대시와 보다 큼 기호입니다.

  • 코드 블록. 코드 블록에 문장이 하나만 있는 경우 중괄호를 생략할 수 있습니다. return 문이 하나만 있는 경우 return을 생략할 수 있으며 람다 식이 자동으로 이 문의 값을 반환합니다.

    return 语句,可以省略 return,lambda表达式会自动返回这条语句的值。

注:
之所以可以省略形参列表是因为 编译器 可以进行类型推断,例如:

List<Dog> dogs1 = new ArrayList<Dog>();

List<Dog> dogs2 = new ArrayList<>();

上面使用 菱形语法,可以省略尖括号里面的东西,这就是类型推断的作用
但是类型推断也不是万能的,不是所有的都可以推断出来的,所以有时候,还是要显示的添加形参类型,例如:

先不要管这个代码的具体作用。

BinaryOperator b = (x, y)->x*y;
//上面这句代码无法通过编译,下面是报错信息:无法将 * 运算符作用于 java.lang.Object 类型。
The operator * is undefined for the argument type(s) java.lang.Object, java.lang.Object
//添加参数类型,正确的代码。
BinaryOperator<Integer> b = (x, y)->x*y;

所以,类型推断不是万能的,如果编译器无法推断,那就是我们的错误,不要过度依赖编译器。有时候,显示的添加参数类型,还是很必要的,当然了,这需要去多练习。

函数式接口

前面了解了,Lambda 表达式可以代替匿名内部类,进而达到简化代码,表达清晰的目的。那么使用 Lambda 表示式的前提是什么呢?-- 函数式接口

Lambda 表达式的类型,也被称为 目标类型 (Target Type),它必须是一个函数式接口(Functional Interface)。所谓函数式接口,指的就是:只包含一个抽象方法的接口。(可以包含多个默认方法,静态方法,但必须只有一个抽象方法)。
注:Java 8 专门提供了一个注解:@FunctionalInterface

🎜🎜참고: 🎜🎜정식 매개변수 목록을 생략할 수 있는 이유는 컴파일러가 유형 추론을 수행할 수 있기 때문입니다. 예: 🎜
ActionListener listener = e->JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);
🎜위에서 다이아몬드 구문이 사용되었으며 꺾쇠 괄호 안의 내용은 다음과 같습니다. 생략 🎜유형 추론 🎜의 역할입니다. 🎜그러나 유형 추론은 전능하지 않으며 모든 것을 추론할 수는 없으므로 때로는 🎜공식 매개변수 유형🎜을 명시적으로 추가해야 하는 경우도 있습니다. 예: 🎜🎜지금은 이 코드의 특정 기능에 대해 걱정하지 마세요. 🎜
import java.text.ParseException;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

public class Test {
	public static void main(String[] args) throws ParseException {
		//Lambda 表达式中的构造器引用,用于简化代码。
		Creat<Dog> c = Dog::new;
		Dog dog = c.creat("小黑", 15);
		System.out.println(dog.toString());
		
		Predicate<String> predicate = (words)->{
			return words.length() > 20;
		};
		assert predicate.test("I love you yesterday and today!") : "长度小于20";
		assert !predicate.test("God bless you!") : "长度小于20";
		System.out.println("------------------------");
		
		Consumer<Dog> consumer = System.out::println;
		consumer.accept(dog);
		System.out.println("------------------------");

		Function<Dog, String> function = (dogObj)->{
			return dogObj.getName();
		};
		System.out.println(function.apply(dog));
		System.out.println("------------------------");
		
		Supplier<Dog> supplier = ()->{
			return new Dog("大黄", 4);
		};
		System.out.println(supplier.get());
		
		//一元操作符
		UnaryOperator<Boolean> unaryOperation = (flag)->{
			return !flag;
		};
		System.out.println(unaryOperation.apply(true));
		
		BinaryOperator<Integer> binaryOperator = (x, y)->x*y;
		int result = binaryOperator.apply(999, 9999);
		System.out.println(result);
	}
}
public class Dog {
	private String name;
	private int age;
	
	public Dog(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Dog [name=" + name + ", age=" + age + "]";
	}
}
🎜🎜따라서 유형 추론은 전능하지 않습니다. 컴파일러가 추론할 수 없다면 이는 우리의 실수입니다. 때로는 매개변수 유형을 명시적으로 추가해야 하는 경우도 있습니다. 물론 이를 위해서는 더 많은 연습이 필요합니다. 🎜🎜🎜기능적 인터페이스🎜🎜앞서 배운 것처럼 Lambda 표현식은 익명의 내부 클래스를 대체하여 코드를 단순화하고 명확하게 표현할 수 있습니다. 그렇다면 람다 표현식을 사용하기 위한 전제 조건은 무엇입니까? -- 🎜Functional Interface🎜🎜🎜Target Type이라고도 불리는 람다 표현식의 유형은 Functional Interface여야 합니다. 소위 기능적 인터페이스는 다음을 의미합니다. 🎜단 하나의 추상 메서드만 포함하는 인터페이스입니다. 🎜(여러 기본 메서드, 정적 메서드를 포함할 수 있지만 추상 메서드는 하나만 있어야 합니다). 🎜참고: Java 8은 @FunctionalInterface라는 특별한 주석을 제공합니다. 인터페이스를 기능적 인터페이스로 표시하여 컴파일 중에 확인하는 데 사용됩니다. 인터페이스에 여러 추상 메서드가 포함되어 있으면 컴파일러에서 오류를 보고합니다. 🎜

上面使用 Lambda 表达式来为按钮添加了监视器,可以看出来,Lambda 表达式 代替了 new ActionListener()对象。

所以 Lambda 的表达式就是被当成一个对象。

例如:

ActionListener listener = e->JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);

从上面这个例子中可以看出来,Lambda 表达式实现的是匿名方法–因此它只能实现特定函数式接口中的唯一方法。
所以 Lambda 表达式有下面两种限制:

Lambda 表达式的目标类型必须是明确的函数式接口。 Lambda 表达式只能为函数式接口创建对象。Lambda只能实现一个方法,因此它只能为含有一个抽象方法的接口(函数式接口)创建对象。 介绍几个 Java 中重要的函数接口

Java Lambda 표현식 사용 사례 분석

从这种表可以看出来,抽象方法的名字反而不是最重要的了,重要的是参数和返回值。 因为在写 Lambda 表达式的时候,也不要使用 抽象方法名了。

上面使用几个简单的例子来说明上面接口的应用:

测试代码

import java.text.ParseException;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

public class Test {
	public static void main(String[] args) throws ParseException {
		//Lambda 表达式中的构造器引用,用于简化代码。
		Creat<Dog> c = Dog::new;
		Dog dog = c.creat("小黑", 15);
		System.out.println(dog.toString());
		
		Predicate<String> predicate = (words)->{
			return words.length() > 20;
		};
		assert predicate.test("I love you yesterday and today!") : "长度小于20";
		assert !predicate.test("God bless you!") : "长度小于20";
		System.out.println("------------------------");
		
		Consumer<Dog> consumer = System.out::println;
		consumer.accept(dog);
		System.out.println("------------------------");

		Function<Dog, String> function = (dogObj)->{
			return dogObj.getName();
		};
		System.out.println(function.apply(dog));
		System.out.println("------------------------");
		
		Supplier<Dog> supplier = ()->{
			return new Dog("大黄", 4);
		};
		System.out.println(supplier.get());
		
		//一元操作符
		UnaryOperator<Boolean> unaryOperation = (flag)->{
			return !flag;
		};
		System.out.println(unaryOperation.apply(true));
		
		BinaryOperator<Integer> binaryOperator = (x, y)->x*y;
		int result = binaryOperator.apply(999, 9999);
		System.out.println(result);
	}
}

测试使用的实体类

public class Dog {
	private String name;
	private int age;
	
	public Dog(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Dog [name=" + name + ", age=" + age + "]";
	}
}

自定义函数式接口

@FunctionalInterface
public interface Creat<T> {
	public T creat(String name, int age);
}

运行截图就不放了,感兴趣的可以试一下。

说明
我这里直接使用 Lambda 创建了对象,然后调用了这个对象的方法(就是lambda 的代码块部分),真正使用的时候,都是直接传递 Lambda 表达式的,这种方法并不推荐,但是可以让我们很好的理解为什么? 可以看出来,Lambda 表达式的作用,最后还是需要调用 重写的抽象方法的,只不过使用表达更加清晰,简化了代码。

例如:

		List<Dog> dogs = new ArrayList<>();
		dogs.add(new Dog("大黄", 2));
		dogs.add(new Dog("小黑", 3));
		dogs.add(new Dog("小哈",1));
		//将行为像数据一样传递,使用集合的 forEach 方法来遍历集合,
		//参数可以是一个 Lambda 表达式。
		Consumer<? super Dog> con = (e)->{
			System.out.println(e);
		};
		dogs.forEach(con);
		System.out.println("--------------------------\n");
	
		//直接传递 Lambda 表达式,更加简洁
		dogs.forEach(e->System.out.println(e));
		System.out.println("--------------------------\n");

		//使用方法引用,进一步简化(可以看我的另一篇关于方法引用的博客)
		dogs.forEach(System.out::println);
		System.out.println("--------------------------\n");

		//使用 Lambda 对集合进行定制排序,按照年龄排序(从小到大)。
		dogs.sort((e1, e2)->e1.getAge()-e2.getAge());
		dogs.forEach(System.out::println);

可以看出来,通过使用 Lambda 表达式可以,极大的简化代码,更加方便的操作集合。值得一提的是:Lambda 表达式 和 Stream 的结合,可以拥有更加丰富的操作,这也是下一步学习的方向。

运行截图:

Java Lambda 표현식 사용 사례 분석

위 내용은 Java Lambda 표현식 사용 사례 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제