Java 8中的:(双冒号)运算符
我在探索Java 8源代码时,发现这部分代码非常令人惊讶:Java 8中的:(双冒号)运算符,java,java-8,Java,Java 8,我在探索Java 8源代码时,发现这部分代码非常令人惊讶: //defined in IntPipeline.java @Override public final OptionalInt reduce(IntBinaryOperator op) { return evaluate(ReduceOps.makeInt(op)); } @Override public final OptionalInt max() { return reduce(Math::max); //th
//defined in IntPipeline.java
@Override
public final OptionalInt reduce(IntBinaryOperator op) {
return evaluate(ReduceOps.makeInt(op));
}
@Override
public final OptionalInt max() {
return reduce(Math::max); //this is the gotcha line
}
//defined in Math.java
public static int max(int a, int b) {
return (a >= b) ? a : b;
}
Math::max
类似于方法指针吗?
一个普通的
静态
方法如何转换成IntBinaryOperator
?是的,确实如此。:
运算符用于方法引用。因此,可以使用类或对象中的方法从类中提取静态方法。即使对于构造函数,也可以使用相同的运算符。下面的代码示例演示了这里提到的所有案例
可以找到Oracle的官方文档
在本文中,您可以更好地概述JDK 8的更改。在方法/构造器引用部分,还提供了一个代码示例:
interface ConstructorReference {
T constructor();
}
interface MethodReference {
void anotherMethod(String input);
}
public class ConstructorClass {
String value;
public ConstructorClass() {
value = "default";
}
public static void method(String input) {
System.out.println(input);
}
public void nextMethod(String input) {
// operations
}
public static void main(String... args) {
// constructor reference
ConstructorReference reference = ConstructorClass::new;
ConstructorClass cc = reference.constructor();
// static method reference
MethodReference mr = cc::method;
// object method reference
MethodReference mr2 = cc::nextMethod;
System.out.println(cc.value);
}
}
这是Java8中的方法引用。oracle文档是 如文件所述 方法引用Person::compareByAge是对静态 方法 下面是对的实例方法的引用示例 特定对象: 方法引用myComparisonProvider::compareByName调用方法compareByName 这是对象myComparisonProvider的一部分。JRE推断出 方法类型参数,在本例中为(Person,Person)
通常,人们会使用
Math.max(int,int)
调用reduce
方法,如下所示:
reduce(new IntBinaryOperator() {
int applyAsInt(int left, int right) {
return Math.max(left, right);
}
});
仅调用Math.max
就需要大量语法。这就是lambda表达式发挥作用的地方。由于Java 8,它可以以更短的方式完成相同的任务:
reduce((int left, int right) -> Math.max(left, right));
这是怎么回事?java编译器“检测”您想要实现一个方法,该方法接受两个int
s并返回一个int
。这相当于interfaceIntBinaryOperator
的唯一方法的形式参数(要调用的方法reduce
的参数)。因此,编译器会为您完成其余的工作—它只是假设您想要实现IntBinaryOperator
但是由于Math.max(int,int)
本身满足IntBinaryOperator
的形式化要求,因此可以直接使用它。因为Java 7没有任何语法允许方法本身作为参数传递(只能传递方法结果,但不能传递方法引用),所以Java 8中引入了:
语法来引用方法:
reduce(Math::max);
请注意,这将由编译器解释,而不是由JVM在运行时解释!尽管它为所有三个代码段生成不同的字节码,但它们在语义上是相同的,因此后两个可以被认为是上述IntBinaryOperator
实现的简短(可能更高效)版本
(另请参见)
:
称为方法引用。它基本上是对单一方法的引用。i、 e.按名称引用现有方法
简短解释:下面是对静态方法的引用示例:
class Hey {
public static double square(double num){
return Math.pow(num, 2);
}
}
Function<Double, Double> square = Hey::square;
double ans = square.apply(23d);
public class TestObject {
public static final TestObject testStatic(TestObject t, TestObject t2){
return t;
}
}
上面的功能
是一个功能界面。要完全理解:
,理解功能接口也很重要。很明显,a是一个只有一个抽象方法的接口
功能接口的示例包括Runnable
、Callable
和ActionListener
上面的Function
是一个功能界面,只有一种方法:apply
。它接受一个参数并产生一个结果
:
s令人敬畏的原因是:
方法引用是与lambda表达式(…)具有相同处理方式的表达式,但它们不是提供方法体,而是按名称引用现有方法
例如,不写lambda主体
Function<Double, Double> square = (Double x) -> x * x;
square
需要一个参数并返回一个double
。中的get
方法返回一个值,但不接受参数。因此,这会导致错误
方法引用是指功能接口的方法。(如上所述,功能接口每个只能有一个方法)
更多示例:中的accept
方法接受输入,但不返回任何内容
Consumer<Integer> b1 = System::exit; // void exit(int status)
Consumer<String[]> b2 = Arrays::sort; // void sort(Object[] a)
Consumer<String> b3 = MyProgram::main; // void main(String... args)
class Hey {
public double getRandom() {
return Math.random();
}
}
Callable<Double> call = hey::getRandom;
Supplier<Double> call2 = hey::getRandom;
DoubleSupplier sup = hey::getRandom;
// Supplier is functional interface that takes no argument and gives a result
对于参数化类型:
class Param<T> {
T elem;
public T get() {
return elem;
}
public void set(T elem) {
this.elem = elem;
}
public static <E> E returnSame(E elem) {
return elem;
}
}
Supplier<Param<Integer>> obj = Param<Integer>::new;
Param<Integer> param = obj.get();
Consumer<Integer> c = param::set;
Supplier<Integer> s = param::get;
Function<String, String> func = Param::<String>returnSame;
类参数{
元素;
公共部门得不到{
返回元素;
}
公共无效集(T元素){
this.elem=elem;
}
公共静态E返回相同(E元素){
返回元素;
}
}
供应商obj=参数::新建;
Param Param=obj.get();
消费者c=param::set;
供应商s=param::get;
函数func=Param::returnSame;
方法引用可以有不同的样式,但从根本上说,它们都意味着相同的东西,可以简单地可视化为lambdas:
ClassName::methName
)instanceRef::methName
)super::methName
)ClassName::methName
)ClassName::new
)TypeName[]::new
)如需进一步参考,请参阅。
:
是Java 8中包含的一个新运算符,用于引用现有类的方法。您可以引用类的静态方法和非静态方法
对于引用静态方法,语法为:
ClassName :: methodName
对于引用非静态方法,语法如下
objRef :: methodName
及
引用方法的唯一先决条件是该方法存在于函数接口中,该接口必须与方法引用兼容
方法引用在求值时创建函数接口的实例
发现
Set<String> set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Predicate<String> pred = set::contains;
boolean exists = pred.test("leo");
class Param<T> {
T elem;
public T get() {
return elem;
}
public void set(T elem) {
this.elem = elem;
}
public static <E> E returnSame(E elem) {
return elem;
}
}
Supplier<Param<Integer>> obj = Param<Integer>::new;
Param<Integer> param = obj.get();
Consumer<Integer> c = param::set;
Supplier<Integer> s = param::get;
Function<String, String> func = Param::<String>returnSame;
ClassName :: methodName
objRef :: methodName
ClassName :: methodName
IntBinaryOperator myLambda = (a, b)->{(a >= b) ? a : b};//56 keystrokes I had to type -_-
return reduce(myLambda);
return reduce(Math::max);//Only 9 keystrokes ^_^
Purchase::calculatePrice
BinaryOperator<TestObject> binary = new BinaryOperator<TestObject>() {
@Override
public TestObject apply(TestObject t, TestObject u) {
return t;
}
};
public class TestObject {
public static final TestObject testStatic(TestObject t, TestObject t2){
return t;
}
}
BinaryOperator<TestObject> binary = TestObject::testStatic;
public class TestObject {
public final TestObject testInstance(TestObject t, TestObject t2){
return t;
}
public static final TestObject testStatic(TestObject t, TestObject t2){
return t;
}
}
TestObject testObject = new TestObject();
BinaryOperator<TestObject> binary = testObject::testInstance;
BinaryOperator<TestObject> binary = TestObject::testInstance;
public class TestObject {
public final TestObject testInstance(TestObject t){
return t;
}
public final TestObject testInstance(TestObject t, TestObject t2){
return t;
}
public static final TestObject testStatic(TestObject t, TestObject t2){
return t;
}
}
BinaryOperator<TestObject> binary = TestObject::testInstance;
public class TestObject {
public TestObject() {
System.out.println(this.hashCode());
}
public final TestObject testInstance(TestObject t){
System.out.println("Test instance called. this.hashCode:"
+ this.hashCode());
System.out.println("Given parameter hashCode:" + t.hashCode());
return t;
}
public final TestObject testInstance(TestObject t, TestObject t2){
return t;
}
public static final TestObject testStatic(TestObject t, TestObject t2){
return t;
}
}
1418481495
303563356
Test instance called. this.hashCode:1418481495
Given parameter hashCode:303563356
public class TestUtil {
public final TestObject testInstance(TestObject t){
return t;
}
}
BinaryOperator<TestObject> binary = TestUtil::testInstance;
public class SubTestObject extends TestObject {
public final TestObject testInstance(TestObject t){
return t;
}
}
BinaryOperator<TestObject> binary = SubTestObject::testInstance;
public class TestObject {
public SubTestObject testInstance(Object t){
return (SubTestObject) t;
}
}
BinaryOperator<TestObject> binary = TestObject::testInstance;
Object :: methodName
Consumer<String> c = s -> System.out.println(s);
Consumer<String> c = System.out::println;
public class Employee {
private String name;
private String grade;
public Employee(String name, String grade) {
this.name = name;
this.grade = grade;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
}
List<Employee> employeeList = getDummyEmployees();
// Using anonymous class
employeeList.sort(new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getGrade().compareTo(e2.getGrade());
}
});
private static List<Employee> getDummyEmployees() {
return Arrays.asList(new Employee("Carrie", "C"),
new Employee("Fanishwar", "F"),
new Employee("Brian", "B"),
new Employee("Donald", "D"),
new Employee("Adam", "A"),
new Employee("Evan", "E")
);
}
employeeList.sort((e1,e2) -> e1.getGrade().compareTo(e2.getGrade())); // lambda exp
public class Employee {
private String name;
private String grade;
// getter and setter
public static int compareByGrade(Employee e1, Employee e2) {
return e1.grade.compareTo(e2.grade);
}
}
employeeList.sort(Employee::compareByGrade); // method reference
+----+-------------------------------------------------------+--------------------------------------+
| | Kind | Example |
+----+-------------------------------------------------------+--------------------------------------+
| 1 | Reference to a static method | ContainingClass::staticMethodName |
+----+-------------------------------------------------------+--------------------------------------+
| 2 |Reference to an instance method of a particular object | containingObject::instanceMethodName |
+----+-------------------------------------------------------+--------------------------------------+
| 3 | Reference to an instance method of an arbitrary object| ContainingType::methodName |
| | of a particular type | |
+----+-------------------------------------------------------+--------------------------------------+
| 4 |Reference to a constructor | ClassName::new |
+------------------------------------------------------------+--------------------------------------+
// We create a comparator of two persons
Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());
// We use the interference
Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());
// The magic using method reference
Comparator c = Comparator.comparing(Person::getAge);
public interface Action {
void execute();
}
public class ActionImpl implements Action {
@Override
public void execute() {
System.out.println("execute with ActionImpl");
}
}
public static void main(String[] args) {
Action action = new Action() {
@Override
public void execute() {
System.out.println("execute with anonymous class");
}
};
action.execute();
//or
Action actionImpl = new ActionImpl();
actionImpl.execute();
}
public static void doSomething(Action action) {
action.execute();
}
InterfaceX f = (x) -> x*x;
interface InterfaceX
{
public Integer callMe(Integer x);
}
interface InterfaceX
{
public Double callMe(Integer x);
}
interface InterfaceX<T,U>
{
public T callMe(U x);
}
InterfaceX o = new InterfaceX(){
public int callMe (int x)
{
return x*x;
} };
class Q {
public static int anyFunction(int x)
{
return x+5;
}
}
InterfaceX o = Q::anyFunction;
InterfaceX o = (x) -> Q.anyFunction(x);
<Class name>::<method name>
// Java code to print the elements of Stream
// without using double colon operator
import java.util.stream.*;
class MyClass {
public static void main(String[] args)
{
// Get the stream
Stream<String> stream
= Stream.of("Testing","Program");
// Print the stream
stream.forEach(s -> System.out.println(s));
}
}
Testing
Program
stream.forEach(System.out::println);