@@ -100,4 +100,136 @@ public class VaraibleHide {
100100<br >lambda表达式和内部类一样,对外部自由变量捕获时,外部自由变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值,
101101同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。大家阅读上面的代码对这些概念应该
102102不难理解。
103- ## 5.[ 方法引用] ( ../方法引用.md )
103+ ## 5.方法引用
104+ ** 只需要提供方法的名字,具体的调用过程由Lambda和函数式接口来确定,这样的方法调用成为方法引用。**
105+ <br >下面的例子会打印list中的每个元素:
106+ ``` java
107+ List<Integer > list = new ArrayList<> ();
108+ for (int i = 0 ; i < 10 ; ++ i) {
109+ list. add(i);
110+ }
111+ list. forEach(System . out:: println);
112+ ```
113+ 其中``` System.out::println ``` 这个就是一个方法引用,等价于Lambda表达式 ``` (para)->{System.out.println(para);} ```
114+ <br >我们看一下List#forEach方法 ``` default void forEach(Consumer<? super T> action) ``` 可以看到它的参数是一个Consumer接口,该接口是一个函数式接口
115+ ``` java
116+ @FunctionalInterface
117+ public interface Consumer <T> {
118+ void accept (T t );
119+ ```
120+ 大家能发现这个函数接口的方法和```System . out:: println```有什么相似的么?没错,它们有着相似的参数列表和返回值。
121+ < br> 我们自己定义一个方法,看看能不能像标准输出的打印函数一样被调用
122+ ```java
123+ public class MethodReference {
124+ public static void main (String [] args ) {
125+ List<Integer > list = new ArrayList<> ();
126+ for (int i = 0 ; i < 10 ; ++ i) {
127+ list. add(i);
128+ }
129+ list. forEach(MethodReference :: myPrint);
130+ }
131+
132+ static void myPrint (int i ) {
133+ System . out. print(i + " , " );
134+ }
135+ }
136+
137+ 输出: 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ,
138+ ```
139+ 可以看到,我们自己定义的方法也可以当做方法引用。
140+ < br> 到这里大家多少对方法引用有了一定的了解,我们再来说一下方法引用的形式。
141+ - 方法引用
142+ - 类名:: 静态方法名
143+ - 类名:: 实例方法名
144+ - 类名:: new (构造方法引用)
145+ - 实例名:: 实例方法名
146+ 可以看出,方法引用是通过(方法归属名):: (方法名)来调用的。通过上面的例子已经讲解了一个`类名:: 静态方法名`的使用方法了,下面再依次介绍其余的几种
147+ 方法引用的使用方法。< br>
148+ ** 类名:: 实例方法名** < br>
149+ 先来看一段代码
150+ ```java
151+ String [] strings = new String [10 ];
152+ Arrays.sort (strings , String ::compareToIgnoreCase );
153+ ```
154+ ** 上面的String::compareToIgnoreCase等价于(x, y) -> {return x. compareToIgnoreCase(y);}** < br>
155+ 我们看一下`Arrays #sort`方法`public static <T > void sort (T [] a , Comparator<? super T > c )`,
156+ 可以看到第二个参数是一个Comparator 接口,该接口也是一个函数式接口,其中的抽象方法是`int compare (T o1 , T o2 );`,再看一下
157+ `String #compareToIgnoreCase`方法,`public int compareToIgnoreCase (String str )`,这个方法好像和上面讲方法引用中`类名::静态方法名`不大一样啊,它
158+ 的参数列表和函数式接口的参数列表不一样啊,虽然它的返回值一样?
159+ <br> 是的,确实不一样但是别忘了,String类的这个方法是个实例方法,而不是静态方法,也就是说,这个方法是需要有一个接收者的。所谓接收者就是
160+ instance.method (x )中的instance,
161+ 它是某个类的实例,有的朋友可能已经明白了。上面函数式接口的`compare (T o1 , T o2 )`中的第一个参数作为了实例方法的接收者,而第二个参数作为了实例方法的
162+ 参数。我们再举一个自己实现的例子:
163+ ```java
164+ public class MethodReference {
165+ static Random random = new Random (47 );
166+ public static void main(String [] args) {
167+ MethodReference [] methodReferences = new MethodReference [10 ];
168+ Arrays . sort(methodReferences, MethodReference :: myCompare);
169+ }
170+ int myCompare(MethodReference o) {
171+ return random. nextInt(2 ) - 1 ;
172+ }
173+ }
174+ ```
175+ 上面的例子可以在IDE 里通过编译,大家有兴趣的可以模仿上面的例子自己写一个程序,打印出排序后的结果。
176+ < br> ** 构造器引用** < br>
177+ 构造器引用仍然需要与特定的函数式接口配合使用,并不能像下面这样直接使用。IDE 会提示String 不是一个函数式接口
178+ ```java
179+ // compile error : String is not a functional interface
180+ String str = String :: new ;
181+ ```
182+ 下面是一个使用构造器引用的例子,可以看出构造器引用可以和这种工厂型的函数式接口一起使用的。
183+ ```java
184+ interface IFunctional <T> {
185+ T func ();
186+ }
187+
188+ public class ConstructorReference {
189+
190+ public ConstructorReference () {
191+ }
192+
193+ public static void main (String [] args ) {
194+ Supplier<ConstructorReference > supplier0 = () - > new ConstructorReference ();
195+ Supplier<ConstructorReference > supplier1 = ConstructorReference :: new ;
196+ IFunctional<ConstructorReference > functional = () - > new ConstructorReference ();
197+ IFunctional<ConstructorReference > functional1 = ConstructorReference :: new ;
198+ }
199+ }
200+ ```
201+ 下面是一个JDK 官方的例子
202+ ```java
203+ public static < T , SOURCE extends Collection<T > , DEST extends Collection<T > >
204+ DEST transferElements(
205+ SOURCE sourceCollection,
206+ Supplier<DEST > collectionFactory) {
207+
208+ DEST result = collectionFactory. get();
209+ for (T t : sourceCollection) {
210+ result. add(t);
211+ }
212+ return result;
213+ }
214+
215+ ...
216+
217+ Set<Person > rosterSet = transferElements(
218+ roster, HashSet :: new );
219+ ```
220+
221+ ** 实例:: 实例方法**
222+ < br>
223+ 其实开始那个例子就是一个实例:: 实例方法的引用
224+ ```java
225+ List<Integer > list = new ArrayList<> ();
226+ for (int i = 0 ; i < 10 ; ++ i) {
227+ list. add(i);
228+ }
229+ list. forEach(System . out:: println);
230+ ```
231+ 其中System . out就是一个实例,println是一个实例方法。相信不用再给大家做解释了。
232+ ## 总结
233+ Lambda 表达式是JDK8 引入Java 的函数式编程语法,使用Lambda 需要直接或者间接的与函数式接口配合,在开发中使用Lambda 可以减少代码量,
234+ 但是并不是说必须要使用Lambda(虽然它是一个很酷的东西)。有些情况下使用Lambda 会使代码的可读性急剧下降,并且也节省不了多少代码,
235+ 所以在实际开发中还是需要仔细斟酌是否要使用Lambda 。和Lambda 相似的还有JDK10 中加入的var 类型推断,同样对于这个特性需要斟酌使用。
0 commit comments