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

我在探索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); //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
。这相当于interface
IntBinaryOperator
的唯一方法的形式参数(要调用的方法
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);