方法引用和构造器引用可以让lambda表达式编码更加简洁,具体语法就是使用2个英文冒号,然后加上方法名字,注意没有括号的。一共有4种情况,类引用类方法,对象引用实例方法,类引用构造器都很好理解,注意的是这里还有一个类引用实例方法。
- 1,引用类方法
public class Test { public static void main(String[] args) { //原始的方法 //Converter converter = str -> Integer.parseInt(str); //使用类方法引用 Converter converter = Integer::parseInt; System.out.println(converter.convert("25")); } } @FunctionalInterface interface { //定义一个将字符串串成integer的方法 Integer convert(String str); }
上面的Lambda表达式的代码块(注释掉的代码)只有一行代码,所以程序可以省略该代码块的花括号,而且由于该函数式接口里面的convert()方法需要返回值,所以Lambda表达式会把这条代码的值作为返回值。现在我们用方法引用来代替Lambda表达式,将Converter接口中被实现方法的全部参数传给该类方法作为参数。比如上面我们自己写的这行代码Converter converter = Integer::parseInt;当我们调用该接口中的唯一的抽象方法时,调用参数将会传给Integer类的parseInt方法。
- 2,引用特定对象的实例方法
public class Test { public static void main(String[] args) { //原始的lambda表达式 //Converter converter = str -> "LinkinPark".indexOf(str); //使用类方法引用 Converter converter = "LinkinPark"::indexOf; System.out.println(converter.convert("kin")); } } @FunctionalInterface interface Converter { //定义一个将获取字符串下表的方法 Integer convert(String str); }上面我们的注释掉的lambda表达式只有一行调用"LinkinPark"的indexOf()实例方法的代码,所以我们使用方法引用来代替。对于上面的实例方法引用,也就是调用字符串对象"LinkinPark"的indexOf()实例方法时,调用参数将会传给"LinkinPark"对象的indexOf()的实例方法。
- 3,引用类的实例方法
public class Test { public static void main(String[] args) { //原始的lambda表达式 // Converter converter = (str, index, end) -> str.substring(index, end); //使用方法引用 Converter converter = String::substring; System.out.println(converter.convert("LinkinPark", 0, 6)); } } @FunctionalInterface interface Converter { /** * @param str 原始的字符串 * @param index 开始截图的下标 * @param end 终止截图的下标 * @return 截取后的字符串 * @Description: 定义一个截取字符串的方法 */ String convert(String str, int index, int end); }这个语法我们在第一次使用的时候觉得有点别扭,记住就好了。针对上面的代码,当我们调用Converter接口中的唯一的抽象方法时,第一个调用参数将作为substring方法的调用者,剩下的调用参数将会作为substring()实例方法的调用参数。另外一点,通过使用super,可以引用方法的父类版本,语法如下:
super::方法名字
- 4,构造器引用
public class Test { public static void main(String[] args) { //原始的lambda表达式 // Converter converter = str -> new String(str); //使用构造器引用 Converter converter = String::new; System.out.println(converter.convert("LinkinPark")); } } @FunctionalInterface interface Converter { //定义一个方法,处理一个字符串然后在返回一个字符串 String convert(String str); }
上面的lambda表达式只有一行代码,而且是调用了某一个类的构造器,所以我们可以使用构造器引用来替换。new就代表使用该类的构造器,这里有一个问题,我们直接使用某一个类的new来表示调用这个类的构造器,那么如果说这个类很多个构造器的时候,应该要使用哪一个构造器呢?这里取决于调用函数式接口里面的抽象方法时传入的参数类型,比如上面代码我们调用Converter类的convert方法,实际传入一个String类型的参数,那么这个参数将会传入到String的使用String类型作为参数的构造器中,上面的构造器引用将调用String类的,带一个String参数的构造器。
- 总结一下:
public class Test { //这其实就是命令者模式 public String test(String str, StringFunc func) { return func.convert(str); } public String test1(StringFuncImpl stringFuncImpl, String str, StringFunc1 func) { return func.convert(stringFuncImpl, str); } public StringFuncImpl test2(String str, StringFunc2 func) { return func.getStringFuncImpl(str); } public static void main(String[] args) { Test test = new Test(); StringFuncImpl stringFuncImpl = new StringFuncImpl(); //下面的代码输出LinkinPark... System.out.println(test.test("LinkinPark", StringFuncImpl::convert)); System.out.println(test.test("LinkinPark", stringFuncImpl::convert1)); System.out.println(test.test1(stringFuncImpl, "LinkinPark", StringFuncImpl::convert1)); System.out.println(test.test2("LinkinPark", StringFuncImpl::new).getName()); } } @FunctionalInterface interface StringFunc { //定义一个接口,一个方法来处理字符串 String convert(String str); } @FunctionalInterface interface StringFunc1 { //定义一个接口,一个方法来处理字符串 String convert(StringFuncImpl stringFuncImpl, String str); } @FunctionalInterface interface StringFunc2 { //得到一个StringFuncImpl实例 StringFuncImpl getStringFuncImpl(String str); } class StringFuncImpl { private String name; public StringFuncImpl() { } public StringFuncImpl(String name) { this.name = name; } public String getName() { return name; } //注意,这里要和上面的函数式接口兼容 static String convert(String str) { return str + "..."; } String convert1(String str) { return str + "..."; } }
- 泛型中的方法引用
public class Test { public static <T> int test(T[] vals, T value, LinkinFunc<T> func) { return func.func(vals, value); } public static void main(String[] args) { Integer[] vals = { 1, 2, 3 }; System.out.println(test(vals, 1, LinkinImpl::<Integer> countMatch)); String[] valss = {"1","2","3"}; System.out.println(test(valss, "1", LinkinImpl::<String> countMatch)); } } @FunctionalInterface interface LinkinFunc<T> { //一个处理泛型数组的算法 int func(T[] vals, T value); } class LinkinImpl { //上面函数式接口的具体实现 static <T> int countMatch(T[] vals, T value) { int count = 0; for (T t : vals) { if (t == value) { count++; } } return count; } }阅读上面的代码没什么难度的,参数的传递发送在::的后面。当把泛型方法指定为方法引用时,参数类型出现在::之后,方法名称之前。需要指出的是在一般情况下,并非必须显示指定类型参数,类型参数会被自动推断得出。
上面写的一些例子只是显示了使用方法引用的机制,并没有展现他的真正优势。方法引用能够一展拳脚的一处地方是在与集合一起时候的时候,比如说现在我们要使用Collections类定义的max()方法来确定集合中最大的元素,以前我们传入一个集合,还要传入一个实现了Comparator<T>接口的对象的实例,匿名内部类,比较麻烦的。现在我们只需要自己实现一个与Comparator接口中定义的compare()方法兼容的方法就可以。比如下面代码:
static int linkinCompare(MyClass a,MyClass b) { retutn a.getId()-b.getId(); }在调用时候直接用类名::linkinCompare就可以了。