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


    前面已经说过了,如果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"));
    	}
    
    
    }
    
    
    @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参数的构造器。


    • 总结一下:
    有一个很重要的特性与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());
    	}
    
    
    }
    
    
    @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就可以了。

  • 相关阅读:
    c#接口和抽象类的区别(转)
    Dephi阿拉伯数字转换成英文和中文大写
    Code Rush Express Template 制作
    SQL中对学习成绩自动排名次
    Resharper上手指南
    如何實現域控制中部分用戶可以寫Programme files目錄的權限?
    阿拉伯数字转换英文数字表示算法解析及其实现
    BOM 算法
    OGRE1.7.1.1vs2008安装
    近一个月工作小总结
  • 原文地址:https://www.cnblogs.com/LinkinPark/p/5232973.html
Copyright © 2020-2023  润新知