方法引用和构造器引用可以让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就可以了。