@@ -100,4 +100,136 @@ public class VaraibleHide {
100
100
<br >lambda表达式和内部类一样,对外部自由变量捕获时,外部自由变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值,
101
101
同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。大家阅读上面的代码对这些概念应该
102
102
不难理解。
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