1、方法引用
/**
* 1.使用場景: 當要傳遞給lambda體的操作,已經有實現的方法了,可以使用方法引用
*
* 2.方法引用本質上就是lambda表達式,而lambda表達式本質上是一個函數式接口的實例,所以方法引用本質上也是函數式接口的實例
*
* 3.使用格式: 類/對象(方法的調用者) :: 方法名
*
* 4.具體分爲如下三種情況!
* 情況一 對象 :: 非靜態方法
* 情況二 類 :: 靜態方法
* 情況三 類 :: 非靜態方法(這個比較特殊!!)
*
* 5.方法引用的要求: 要求接口中抽象方法的形參列表和返回值類型,與方法引用的方法的形參列表和返回值類型相同!
* 例如:Consumer接口中的void accept(T t);
* 如果你想在這裏使用方法引用,則調用的該方法,必須和接口中抽象方法的參數列表和返回值類型一致!
* 所以這裏: void setxxx(T t) 是ok的, 而String getxxx()是不行的!!!
* 注:這個要求,只適用用情況一,二!
*/
public class MethodRefTest {
/**
* 情況一: 對象 :: 實例方法
* Consumer中的void accpet(T t);
* PrintStream中的void println(T t);
*/
@Test
public void test1(){
//一: 使用lambda表達式
Consumer<String> consumer = str -> System.out.println(str);
consumer.accept("大帥比");
//二:使用方法引用!
PrintStream ps = System.out;
Consumer<String> consumer2 = ps::println;
consumer2.accept("dashuaibi");
}
/**
* Supplier中的 T get()
* 相當於Employee中的String getName();
*/
@Test
public void test1_1(){
//一:使用lambda表達式
Employee employee = new Employee(1001,"wzj",22,50000);
Supplier<String> supplier = () -> employee.getName();
System.out.println(supplier.get());
//二:使用方法引用!
Supplier<String> supplier2 = employee::getName;
System.out.println(supplier2.get());
}
/**
* 情況一: 類 :: 靜態方法
* Comparator接口中的int compare(T o1, T o2);
* Integer中的int compare(T o1, T o2)
*/
@Test
public void test2(){
//方式一: lambda表達式
Comparator<Integer> c1 = (o1, o2) -> Integer.compare(o1,o2);
System.out.println(c1.compare(1,2));
//方式二: 方法引用
Comparator<Integer> c2 = Integer::compareTo;
System.out.println(c2.compare(1,2));
}
/**
* Function接口是1.8出的!
* Function中的R apply(T t);
* Math中的 long round(Double d);
*注:就是傳進去是這個類型,返回出來去另一個類型!
*/
@Test
public void test2_2(){
//方式一:原生寫法
Function<Double,Long> function = new Function<Double, Long>() {
@Override
public Long apply(Double d) {
return Math.round(d);
}
};
System.out.println(function.apply(12.0));
//方式二: lambda表達式
Function<Double,Long> function2 = d -> Math.round(d);
System.out.println(function.apply(12.0));
//方式三: 方法引用
Function<Double,Long> function3 = Math::round;
System.out.println(function.apply(12.0));
}
/**
* 情況三 類 :: 實例方法 (有難度)
* Comparator接口中的int compare(T t1, T t2)
* String中的 int t1.compareTo(t2)
*
* 注:雖然上面參數不匹配,但是上面是拿第一個參數的類型作爲調用者!
*/
@Test
public void test3(){
//方式一: lambda表達式
Comparator<String> c1 = (t1,t2) -> t1.compareTo(t2);
System.out.println(c1.compare("abc","abc"));
//方法引用: 這裏就相當於,將T1的類型,作爲調用者來調用compareTo(t2)方法
Comparator<String> c2 = String :: compareTo;
System.out.println(c2.compare("abc","abc"));
}
/**
* BiPredicate(1.8出現的)
* BiPredicate中的 boolean test(T t1, T t2)
* String中的 boolean t1.equals(t2)
*/
@Test
public void test3_3(){
//方式一: lambda表達式
BiPredicate<String,String> b = (t1, t2) -> t1.equals(t2);
System.out.println(b.test("abc","abc"));
//方法引用: 這裏就相當於,將T1的類型,作爲調用者來調用compareTo(t2)方法
BiPredicate<String,String> b2 = String::equals;
System.out.println(b2.test("abc","abc"));
}
/**
* Function接口(1.8出現的)
* Function中的 R apply(T t);
* Employee中的String getName()
*/
@Test
public void test3_4(){
Employee employee = new Employee(007,"wzj",22,100000);
//方式一: lambda表達式
Function<Employee,String> f = e -> e.getName();
System.out.println(f.apply(employee));
//方法引用: 這裏就相當於,將T1的類型,作爲調用者來調用compareTo(t2)方法
Function<Employee,String> f2 = Employee::getName;
System.out.println(f2.apply(employee));
}
}
演示
類 :: 非靜態方法
(這個是最特別的)
/**
* 方法引用: 對象::實例方法,靜::靜態方法
* 我們都說:引用方法的,參數和返回值,必須和抽象方法的參數和返回值一一對應,
*
* 演示特殊的情況: 類::非靜態方法
* 非靜態方法的參數和返回值,和抽象方法不一致,也不影響使用方法引用!!
*
* 例子如下:
*/
@Test
public void test3(){
/**
* 1. Object類的toString()
* String toString()
* 2.函數式接口(Function)的抽象方法
* R apply(T t);
*
* 很明顯:toString方法和apply()抽象方法,參數不對應
* 那麼這時apply(T t)抽象方法的形參t,就會作爲調用者,來調用toString()
*
* 總結: 抽象方法單參有返回 --通過類::實例方法--> 無參有返回!
*/
String s1 = Arrays.asList("aa", "bb").stream().map(new Function<String, String>() {
@Override
public String apply(String s) {
return s.toString();
}
}).collect(Collectors.joining("+"));
System.out.println(s1);
String s2 = EmployeeData.getEmployees().stream().map(Object::toString).collect(Collectors.joining("+"));
System.out.println(s2);//Employee(id=1001, name=wzj, age=22, salary=50000.0)+Employee(id=1002, name=tom, age=22, salary=40000.0) 我只用了集合中的兩個元素演示一下!
}
/**
* 1.String類中的實例方法
* int compareTo(String anotherString)
* 2.函數式接口(Comparator)的抽象方法
* int compare(String o1, String o2)
*
* 很明顯:compareTo方法和compare()抽象方法,參數不對應
* 那麼這時抽象方法compare中的參數o1,就會作爲調用者來調用方法compareTo,將參數2,o2作爲形參傳進去!!
*
* 總結:抽象方法多參有返回 --通過類::實例方法--> 單參有返回!!
*/
@Test
public void test4(){
Comparator<String> c1 = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
};
System.out.println(c1.compare("1","2")); // -1
//演示方法引用之,類::實例方法
Comparator<String> c2 = String::compareTo;
System.out.println(c2.compare("2","1")); // 1
}
2、構造器引用
@Data
class Employee {
private int id;
private String name;
public Employee() {}
public Employee(int id) {
this.id = id;
}
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
}
/**
* 構造器引用:
* 和方法引用類似
* 函數式接口的抽象方法的形參列表,和構造器的形參列表一致
* 抽象方法的返回值類型,即爲構造器所屬的類的類型!
*
* 數組引用!
* 這裏可以將數組引用看做成一個特殊的類,則寫法和構造器引用就一致了
*
* 使用場景;就是一個抽象方法的返回值類型是一個類就可以用構造器引用,是一個數組就用數組引用!
*
* 總結:lambda表達式是函數式接口的實例,其實方法引用,構造器引用可以替換lambda表達式,所以它們也是函數式接口的實例!
* 方法引用,構造器引用不會-->就寫lambda表達式,lambda不會--->就寫原生的
*/
public class ConstructorRefTest {
/**
* 構造器引用之無參構造
* @FunctionalInterface
* Supplierr<T>{
* T get();
* }
*/
@Test
public void test3(){
// 1.匿名內部類
Supplier<Employee> s1 = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println(s1.get()); //Employee(id=0, name=null)
// 2.lambda表達式
Supplier<Employee> s2 = () -> new Employee();
System.out.println(s2.get()); //Employee(id=0, name=null)
// 3.構造器引用
Supplier<Employee> s3 = Employee::new;
System.out.println(s3.get()); //Employee(id=0, name=null)
}
/**
* 構造器引用之單參構造
* @FunctionalInterface
* Function<T,R>{
* R apply(T);
* }
*/
@Test
public void test4(){
// 1.匿名內部類
Function<Integer,Employee> fun1 = new Function<Integer, Employee>() {
@Override
public Employee apply(Integer id) {
return new Employee(id);
}
};
System.out.println(fun1.apply(007)); //Employee(id=7, name=null)
// 2.lambda表達式
Function<Integer,Employee> fun2 = id -> new Employee(id);
System.out.println(fun2.apply(007)); //Employee(id=7, name=null)
//3. 構造器引用
Function<Integer,Employee> fun3 = Employee::new;
System.out.println(fun3.apply(007)); //Employee(id=7, name=null)
}
/**
* 構造器引用之多參構造
* @FunctionalInterface
* public interface BiFunction<T, U, R> {
* R apply(T t, U u);
* }
*/
@Test
public void test5(){
// 1.匿名內部類
BiFunction<Integer,String,Employee> b1 = new BiFunction<Integer, String, Employee>() {
@Override
public Employee apply(Integer id, String name) {
return new Employee(id,name);
}
};
System.out.println(b1.apply(007,"wzj")); //Employee(id=7, name=wzj)
// 2.lambda表達式
BiFunction<Integer,String,Employee> b2 = (id,name) -> new Employee(id,name);
System.out.println(b2.apply(007,"wzj")); //Employee(id=7, name=wzj)
// 3.構造器引用
BiFunction<Integer,String,Employee> b3 = Employee::new;
System.out.println(b3.apply(007,"wzj")); //Employee(id=7, name=wzj)
}
}
3、數組引用:
/**
* 數組引用
* Function中的 R apply(T t)
*
* 1.先看接口的泛型,並指定好
* 2.然後再接口中該抽象方法需要傳入的泛型是哪個,用一個參數代替就行
*/
@Test
public void test1(){
// 1.匿名內部類
Function<Integer,String[]> fun1 = new Function<Integer, String[]>() {
@Override
public String[] apply(Integer i) {
return new String[i];
}
};
System.out.println(Arrays.toString(fun1.apply(3))); // [null, null, null]
// 2.lambda表達式
Function<Integer,String[]> fun2 = (i) -> new String[i];
System.out.println(Arrays.toString(fun2.apply(3))); // [null, null, null]
//3.數組引用
//注: 由於函數式接口中的抽象方法的返回值是數組,所以這裏使用了數組引用
Function<Integer,String[]> fun3 = String[]::new;
//注:數組不能直接使用foreach()遍歷,需要先轉換成流然後調用foreach()
Stream<String> strStream = Arrays.stream(fun2.apply(3));
//注: System.out::prinln是方法引用(對象::非靜態方法)
strStream.forEach(System.out::println); //null,null,null 將元素一個個打印出來了!
}
/**
* 需求:從集合中篩選姓“張”的存入到數組中 !!
*/
@Test
public void test2(){
// 1.創建集合
List<String> strList = new ArrayList<>(Arrays.asList("張三","張飛","李四"));
// 2.將集合轉換成流, 因爲Collection接口中有stream()方法!
/**
* 下面調用的是帶參的toArray()
* <A> A[] toArray(IntFunction<A[]> generator);
*
* 函數式接口
* IntFunction<R>{
* R apply(int value);
* }
*
* 抽象方法單參有返回: R apply(int value);
* 數組引用: 無參有返回, 只將創建的數組作爲返回值返回了!, 神奇的是元素還已經裝進了數組中!
*/
String[] ss = strList.stream().filter(str -> str.startsWith("張")).toArray(String[]::new);
System.out.println(Arrays.toString(ss)); //[張三, 張飛]
}
總結
1.不管是原生的匿名內部類,還是lambda表達式,還是方法引用,構造器引用,數組引用.本質上都是函數式接口的實現類對象!
2.構造器引用和數組引用的使用場景是一個抽象方法的返回值是一個類/數組,此時可以使用
3.構造器引用(Employee::new)和數組引用(String[]::new)的使用語法差不多,可以反過來看不就是new Employee, new String[]
4.一般來說如果想使用方法引用,那麼必須保證函數式接口中的抽象方法的參數和返回值的個數和類型,與方法引用的參數和返回值的個數和類型應該保持一一對應!
5.但是注意有特殊的!!比如:方法引用(類::實例方法),構造器引用,數組引用. 它們的參數和返回值個數和類型與抽象方法的參數和返回值的個數和類型沒有保持一般,仍然可以調用
比如:
①: 函數式接口中的抽象方法假如是單參有返回值,使用類::實例方法,構造器引用,數組 就可以做到無參有返回值
②: 函數式接口中的抽象方法假如是多參有返回值,使用類::實例方法,構造器引用,數組 就可以做到單參有返回值
來自:雖然帥,但是菜的cxy,每天進步一點點 peace