函数式编程--方法和构造器引用

来源:互联网 发布:东莞厂货aj哪淘宝店好 编辑:程序博客网 时间:2024/06/06 01:03
前面已经说过了,如果lambda表达式的代码块只有一行代码,可以省略lambda表达式中代码块的花括号,语句后面的逗号也要省去的。不仅如此,如果lambda表达式的代码块只有一行代码,还可以在代码中使用方法引用和构造器引用。

方法引用和构造器引用可以让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"));}}@FunctionalInterfaceinterface  {//定义一个将字符串串成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"));}}@FunctionalInterfaceinterface 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));}}@FunctionalInterfaceinterface 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"));}}@FunctionalInterfaceinterface Converter{//定义一个方法,处理一个字符串然后在返回一个字符串String convert(String str);}

上面的lambda表达式只有一行代码,而且是调用了某一个类的构造器,所以我们可以使用构造器引用来替换。new就代表使用该类的构造器,这里有一个问题,我们直接使用某一个类的new来表示调用这个类的构造器,那么如果说这个类很多个构造器的时候,应该要使用哪一个构造器呢?这里取决于调用函数式接口里面的抽象方法时传入的参数类型,比如上面代码我们调用Converter类的convert方法,实际传入一个String类型的参数,那么这个参数将会传入到String的使用String类型作为参数的构造器中,上面的构造器引用将调用String类的,带一个String参数的构造器。


  • 总结一下:
有一个很重要的特性与lambda表达式相关,叫做方法引用。方法引用提供了一种引用而不执行方法的方式。使用方法引用,需要由兼容的函数式接口构成的目标类型的上下文,计算时,方法引用也会创建函数式接口的一个实例。上面的几个例子我们都是直接使用现成的jdk里面的类,现在我们自己定义几个类和方法来练习下方法引用和构造器引用。看下面代码:

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());}}@FunctionalInterfaceinterface StringFunc{//定义一个接口,一个方法来处理字符串String convert(String str);}@FunctionalInterfaceinterface StringFunc1{//定义一个接口,一个方法来处理字符串String convert(StringFuncImpl stringFuncImpl, String str);}@FunctionalInterfaceinterface 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));}}@FunctionalInterfaceinterface 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就可以了。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 建行手机银行登录密码忘了怎么办 手机银行登入密码忘记了怎么办 邮政手机银行登录密码忘了怎么办 建设手机银行登入密码忘记了怎么办 浪琴机械表秒针走的快怎么办 雷达晶萃陶瓷表镀金掉色怎么办 做信息稿部分人员没拍到照片怎么办 二建条件不够考后审核怎么办 学校官网的教务系统忘记密码怎么办 已参加两次高考失败还想复读怎么办 我高考失利想补习学藉怎么办 本科毕业证上是1寸照片怎么办 老婆父母不给户口本迁户口怎么办 深圳夫妻投靠双方再婚的网上怎么办 老人档案丢了要继承公证怎么办 农民把户口迁入城市后宅基地怎么办 离婚了再婚带孩子在上海上学怎么办 上班几天被公司辞退不发工资怎么办 在单位工作被领导边缘化该怎么办 退休人员户口迁到外地退休金怎么办 招工表填写和实际的有误怎么办 招工时档案年龄有人为改动怎么办 8个月宝贝还不会坐怎么办 朗动导航黑屏过了保修期怎么办 平板突然黑屏开不了机了怎么办 苹果ipad锁屏密码忘了怎么办 公司发资以两张工资表怎么办 面试谈好的工资结果发少了怎么办 老板面试没谈薪资让等电话怎么办 土地局把我的档案弄丢了怎么办 人事关系在医院不去上班怎么办 档案年龄多处涂改无法退休怎么办 个人办理退休原始档案丢了怎么办 在钢厂工作想辞职了怎么办呢 场地合同到期了公司不续签怎么办 应届生没毕业找到工作怎么办入职 公司工资保密同事要看工资条怎么办 考上特岗教师后和老公异地怎么办 档案在无档案保管权限的公司怎么办 辞职了户口迁回农原籍不接收怎么办 当正职直接指挥下级副职领导怎么办