Java8是Java的一个核心版本,添加了许多新的功能和操作,也是Java中使用最高的一个版本即使现在Java16都已经出了。不少人依然对Java8这些特性不了解,甚至看不懂代码。就让本篇文章带你去了解下这些新特性
在写这个文章之前,我对此只是略有了解,甚至写不出一个简单的lambda语句,现在基本上lambda语句都能写出来了,sream系列操作因为涉及到很多方法,写不出来所有的,但是常用的几种流的生成,中间操作和最终操作还是能写出来的。::表达式也基本能看懂,下面就跟着我来了解下吧
lambda表达式的表现形式:
()->{};
lambda表达式的格式:
() 用来写参数的, 对应抽象方法中()的参数类型及声明直接copy过来
-> 用来进行参数传递
{} 重写方法的代码都写到大括号中,对应匿名内部类{}重写方法体直接copy过来
lambda表达式要求:
lambda表达式的省略:
代码演示循序渐进
//0lambda的标准样式
TestLambdaOmit testLambdaOmit0 = (String aa, String bb) -> {
System.out.println("0lambda的标准样式");
return aa + bb;
};
//1lambda参数类型声省略掉(常用)
TestLambdaOmit testLambdaOmit1 = (aa, bb) -> {
System.out.println("1lambda参数类型声省略掉");
return aa + bb;
};
//2()中如果只有一个参数,小括号可以省略
TestLambda testLambda2 = (aa -> {
System.out.println("2()中如果只有一个参数,小括号可以省略");
});
//3{} 号中如果只有一行代码,大括号可以省略,分号也可以省略
TestLambda testLambda3 = (aa -> System.out.println("3{} 号中如果只有一行代码,大括号可以省略,分号也可以省略"));
//4{}当只有一行返回值的时候,在大括号和分号同时省略的基础上,return 也可以省略
TestLambdaOmit testLambdaOmit4 = (aa, bb) -> aa + bb;
// System.out.println("4{}当只有一行返回值的时候在大括号和分号同时省略的基础上,return 也可以省略");
完整版代码
package com.concurrent.demo15Lambda;
/**
* lambda
* @author lane
* @date 2021年05月26日 下午11:21
*/
public class TestLambdaDemo {
public static void main(String[] args) {
//会出错,没有上下文,推断不出来
// ()->{ System.out.println("aa");};
//匿名内部类
TestLambda testLambda = new TestLambda() {
@Override
public void test(String aa) {
System.out.println("test方法执行了");
}
};
testLambda.test("hello");
//必须只有一个抽象方法才行,接口中如果有两个方法就会编译出错了
//lambda
TestLambda testLambda1 = ((aa) -> {
System.out.println("lambda的test方法执行了");
});
testLambda1.test("hello");
/* lambda表达式的省略(不建议看)
1. () 中的参数类型声明可以省略掉,可以推断出来
2. () 中如果只有一个参数,小括号可以省略
3. {} 号中如果只有一行不是返回值的代码,大括号可以省略,分号也可以省略(必须分号和大括号同时省略)
4. {} 当只有一行返回值的时候,在大括号和分号同时省略的基础上,return 也可以省略
*/
//0lambda的标准样式
TestLambdaOmit testLambdaOmit0 = (String aa, String bb) -> {
System.out.println("0lambda的标准样式");
return aa + bb;
};
//1lambda参数类型声省略掉
TestLambdaOmit testLambdaOmit1 = (aa, bb) -> {
System.out.println("1lambda参数类型声省略掉");
return aa + bb;
};
//2()中如果只有一个参数,小括号可以省略
TestLambda testLambda2 = (aa -> {
System.out.println("2()中如果只有一个参数,小括号可以省略");
});
//3{} 号中如果只有一行代码,大括号可以省略,分号也可以省略
TestLambda testLambda3 = (aa -> System.out.println("3{} 号中如果只有一行代码,大括号可以省略,分号也可以省略"));
//4{}当只有一行返回值的时候,在大括号和分号同时省略的基础上,return 也可以省略
TestLambdaOmit testLambdaOmit4 = (aa, bb) -> aa + bb;
// System.out.println("4{}当只有一行返回值的时候在大括号和分号同时省略的基础上,return 也可以省略");
//0
System.out.println("========下面是测试省略============");
System.out.println(testLambdaOmit0.testOmit("hello", "world"));
//1
System.out.println(testLambdaOmit1.testOmit("hello", "world"));
//2
testLambda2.test("hello");
//3
testLambda3.test("hello");
//4
System.out.println(testLambdaOmit4.testOmit("hello", "world"));
}
}
/*
test方法执行了
lambda的test方法执行了
========下面是测试省略============
0lambda的标准样式
helloworld
1lambda参数类型声省略掉
helloworld
2()中如果只有一个参数,小括号可以省略
3{} 号中如果只有一行代码,大括号可以省略,分号也可以省略
helloworld
*/
只有一个抽象方法的接口,称之为函数式接口,天生就是为了适合lambda,default关键字也是为了lambda的
一般用@FunctionalInterface注解标注
接口中default关键字修饰方法
1.8 新引入的Lambda表达式,有明显的局限性只能有一个抽象方法很不好,如果想实现多个方法呢,lambda就不可以使用了,为了让lambda可以使用,某种程度也可以重写多个方法,所有就有了default方法(Virtual extension methods)。
default方法是指,在接口内部包含了一些默认的方法实现(也就是接口中可以包含方法体,这打破了Java之前版本对接口的语法限制),从而使得接口在进行扩展的时候,直接就重写了自己的default方法了
简而言之就是接口中的方法既可以有抽象方法也可以有default修饰的完整方法
/**
* @author lane
* @date 2021年05月26日 下午11:20
*/
public interface TestLambda {
//抽象方法
public abstract void test(String aa);
//public abstract void testTwo();
//含有方法体的方法
default void testDefault(){
System.out.println("接口中default的方法执行了");
}
}
/**
* 既可以使用lambda又可以有多个方法
* @author lane
* @date 2021年05月27日 上午1:04
*/
public class DefaultDemo {
public static void main(String[] args) {
TestLambda testLambda = (s)->{
System.out.println("lambda执行接口test方法");
};
testLambda.test("helloworld");
testLambda.testDefault();
}
}
/*
lambda执行接口test方法
接口中default定于的方法执行了
*/
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
// jdk以前的核心特性: 泛型、枚举、反射等
// 新的特性:lambda表达式、链式编程、函数式接口、Stream流式计算
// 超级多FunctionalInterface
// 简化编程模型,在新版本的框架底层大量应用!
// foreach(消费者类的函数式接口)
Java.util.function下的四大函数式接口
有一个输入参数,有一个输出
代码实现Function
package com.concurrent.demo13Function;
import java.util.function.Function;
/**
* Function 函数型接口, 有一个输入参数,有一个输出
* 只要是 函数型接口 可以 用 lambda表达式简化
* @author lane
* @date 2021年05月26日 下午4:40
*/
public class FunctionDemo {
public static void main(String[] args) {
//匿名内部类实现
Function<Integer,String> function = new Function<Integer,String> () {
@Override
public String apply(Integer i) {
System.out.println("执行了apply方法"+i);
return "re:"+i;
}
};
//lambda实现
Function<Integer,String> function2 = (integer)->{
System.out.println("执行了apply方法"+integer);
return "re"+integer;
};
function.apply(1024);
System.out.println("===========================");
function2.apply(2048);
}
}
/*
执行了apply方法1024
===========================
执行了apply方法2048
*/
断定型接口:有一个输入参数,返回值只能是 布尔值!
代码实现
package com.concurrent.demo13Function;
import java.util.function.Predicate;
/**
* 有一个参数,返回值只能是布尔
* @author lane
* @date 2021年05月26日 下午7:10
*/
public class PredicateDemo {
public static void main(String[] args) {
//匿名内部类方式
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String s) {
System.out.println("执行了predicate方法");
return s.isEmpty();
}
};
System.out.println(predicate.test("1024"));
//lambda方式实现
Predicate<String> predicate2 = (string)->{
System.out.println("lambda执行了predicate方法");
return string.isEmpty();};
System.out.println(predicate2.test(""));
}
}
/*
执行了predicate方法
false
lambda执行了predicate方法
true
*/
只有一个输入,没有返回值的接口
代码实现
package com.concurrent.demo13Function;
import java.util.function.Consumer;
/**
* 消费型接口只有输入没有返回值
* @author lane
* @date 2021年05月26日 下午7:17
*/
public class ConsumerDemo {
public static void main(String[] args) {
Consumer<String> consumer = new Consumer<String>(){
@Override
public void accept(String s) {
System.out.println("内部类执行了accept方法"+s);
}
@Override
public Consumer<String> andThen(Consumer<? super String> after) {
return null;
}
};
consumer.accept("1024");
Consumer<String> consumer1 = (s->{
System.out.println("lambda执行了consumer方法"+s);
});
consumer1.accept("1024");
}
}
/*
内部类执行了accept方法1024
lambda执行了consumer方法1024
*/
没有参数值,只有一个返回值
代码实现
package com.concurrent.demo13Function;
import java.util.function.Supplier;
/**
* Supplier 供给型接口 没有参数,只有返回值
* @author lane
* @date 2021年05月26日 下午7:26
*/
public class SupplierDemo {
public static void main(String[] args) {
Supplier<String> supplier = new Supplier<String>(){
@Override
public String get() {
System.out.println("匿名内部类执行get方法");
return "1024";
}
};
supplier.get();
Supplier<String> supplier1 =()->{
System.out.println("lambda执行get方法");
return "1024";
};
supplier1.get();
}
}
/*匿名内部类执行get方法
lambda执行get方法
*/
看到这里,你可能有疑问,这都是什么啊。 实际这就是lambda表达式和stream的核心。这类接口就是Java 特意为lambda来创建的函数式接口,凡是这类接口都可以写成lambda的形式,这类接口也是stream流操作的核心参数。
Stream介绍
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等
Stream特性
注意事项: Stream 的操作基本是函数式接口一般以 lambda 表达式或者::表达式为参数。
Stream 流操作类型
Stream使用
在开发中,我们有时需要对一些数据进行过滤,如果是传统的方式,我们需要对这批数据进行遍历过滤,会显得比较繁琐,如果使用steam流方式的话,那么可以很方便的进行处理。
个人理解
stream流式表达式的结构
生成流的方法,一般如list.stream()
中间操作方法,如fileter(Predicate) 可以多个
终结方法,如foreach(Consumer) 只能有一个
Predicate和Consumer是否很眼熟呢
stream流的执行顺序
可以理解为 产品原材料---->流水线工位—>流水线开关 当工位准备完成后,流水线才会打开,才会在具体工位执行操作
stream流的执行原理
匿名内部类的延时执行现象
代码实现流式原理
package com.concurrent.demo14Stream;
import java.util.Objects;
import java.util.function.Predicate;
/**
* 匿名内部类的延时执行特性
* @author lane
* @date 2021年05月27日 上午12:47
*/
public class StreamDemo2 {
public static void main(String[] args) {
Predicate<String> predicate1 = new Predicate<String>() {
@Override
public boolean test(String s) {
System.out.println("1执行了test方法了,参数为"+s);
return s.endsWith("d");
}
};
Predicate<String> predicate2 = new Predicate<String>() {
@Override
public boolean test(String s) {
System.out.println("2执行了test方法了,参数为"+s);
return s.startsWith("h");
}
};
mymethod(predicate1,predicate2);
}
public static void mymethod(Predicate<String> pre1,Predicate<String> pre2){
//源码 返回的是函数时接口
/*default Predicate and(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}*/
//1
pre1.and(pre2);
//2
new Predicate<String>() {
@Override
public boolean test(String s) {
System.out.println("执行了test方法了,参数为"+s);
return s.startsWith("h") && s.endsWith("d");
}
};
//看源码得知1等价于2,这样执行并不会打印任何信息
//而stream流的创建和过滤,只是拼接条件并没有执行具体方法 依据匿名内部类的延时执行现象
//多个拼接的方法并没有执行,类似于stream流式多个过滤方法
//调用test方法就类似于stream流的终结方法,这时候才会执行所有的方法
//list.stream().filter(s->s.startsWith("h")).filter(s->s.endsWith("d")).foreach((s)->System.out.println("执行了test方法了,参数为"+s);)
pre1.and(pre2).test("helloworld");//这就是流的原理一种表现形式
//打印内容为
//1执行了test方法了,参数为helloworld
//2执行了test方法了,参数为helloworld
//源码
/*default Predicate and(Predicate super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}*/
}
}
代码实现流式操作
package com.concurrent.demo14Stream;
import java.util.Arrays;
import java.util.List;
/**
* 题目要求:一分钟内完成此题,只能用一行代码实现!
* 现在有6个用户!筛选:
* 1、ID 必须是偶数
* 2、年龄必须大于23岁
* 3、用户名转为大写字母
* 4、用户名字母倒着排序
* 5、只输出两个用户!
* @author lane
* @date 2021年05月26日 下午7:42
*/
public class StreamDemo {
public static void main(String[] args) {
User user1 = new User(1,"zhangsan",25);
User user2 = new User(2,"lisi",27);
User user3 = new User(3,"wangwu",26);
User user11 = new User(4,"zhangsan",28);
User user22 = new User(5,"lisi",15);
User user33 = new User(6,"wangwu",23);
List<User> userList = Arrays.asList(user1, user2, user3,user11,user22,user33);
userList.stream()
//参数为predicate有参数,返回布尔
.filter((user)->{return user.getId()%2==0;})
.filter((user)->{return user.getAge()>23;})
//参数为function有参数和返回值
.map(user -> {return user.getName().toUpperCase();})
.sorted((u1,u2)->{return u2.compareToIgnoreCase(u1);})
.limit(2)
//返回值是consumer有个参数,无返回值
.forEach((user)->{
System.out.println(user);
});
}
}
/*
ZHANGSAN
LISI
*/
可以看到strem常用的参数都是我们上一部分的函数式接口predicate
,实际上stream的操作方法有很多很多,自己慢慢学,这里只是简单入门方法。可以参考JDK1.8的Lambda、Stream和日期的使用详解来学习更多的stream使用
英文:double colon,双冒号(::)运算符在Java 8中被用作方法引用(method reference),方法引用是与lambda表达式相关的一个重要特性。它提供了一种不执行方法的方法。为此,方法引用需要由兼容的函数接口组成的目标类型上下文。
方法引用
您使用lambda表达式创建匿名方法。 但是,有时lambda表达式除了调用现有方法外什么也不做。 在这种情况下,通常更容易按名称引用现有方法。 方法引用使您可以执行此操作; 它们是紧凑,易于阅读的lambda表达式,用于已经具有名称的方法。
摘自oracle官网
::
关键字提供了四种语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda
联合使用,::
关键字可以使语言更简洁,减少冗余代码。
语法种类 | 示例 |
---|---|
引用静态方法 | ContainingClass::staticMethodName |
引用特定对象的实例方法 | containingObject::instanceMethodName |
引用特定类型的任意对象的实例方法 | ContainingType::methodName |
引用构造函数 | ClassName::new |
常用的一种场景是
把方法当做参数传到stream内部,使stream的每个元素都传入到该方法里面执行一下。
个人理解
官方的例子是伪代码,代码我修改一下后的
代码实例
package com.concurrent.demo16;
import java.time.LocalDate;
import java.util.Calendar;
/**
* @author lane
* @date 2021年05月27日 下午7:37
*/
public class Person {
public enum Sex {
MALE, FEMALE
}
String name;
Integer birthday;
Sex gender;
String emailAddress;
public int getAge() {
// ...
return 1024;
}
public Integer getBirthday() {
return birthday;
}
public static int compareByAge(Person a, Person b) {
return a.birthday.compareTo(b.birthday);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setBirthday(Integer birthday) {
this.birthday = birthday;
}
public Sex getGender() {
return gender;
}
public void setGender(Sex gender) {
this.gender = gender;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
}
测试类
package com.concurrent.demo16;
import java.util.*;
/**
* @author lane
* @date 2021年05月27日 下午7:03
*/
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
List<Person> list = new ArrayList<>();
p1.setBirthday(20);p2.setBirthday(12);p3.setBirthday(22);
list.add(p1);
list.add(p2);
list.add(p3);
Person[] rosterAsArray = list.toArray(new Person[list.size()]);
class PersonAgeComparator implements Comparator<Person> {
public int compare(Person a, Person b) {
return a.getBirthday().compareTo(b.getBirthday());
}
}
//工具类排序方法
// static void sort(T[] a, Comparator super T> c)
//方式一:创建类PersonAgeComparator重写接口
Arrays.sort(rosterAsArray, new PersonAgeComparator());
//方式二:lambda
Arrays.sort(rosterAsArray,
(Person a, Person b) -> {
return a.getBirthday().compareTo(b.getBirthday());
}
);
//方式三:最简化版lambda,参数类型可以略,只有一行的时候可以省略掉 return 和 {}
Arrays.sort(rosterAsArray, (a,b) -> a.getBirthday().compareTo(b.getBirthday()));
//方式四:可以是随便一个类的类似方法,只要推断没错
Arrays.sort(rosterAsArray, (a, b) -> Person.compareByAge(a, b));
//可以是随便一个类的类似方法,只要推断没错
Arrays.sort(rosterAsArray, (a, b) -> Human.compareByAge(a, b));
//方式五:::表达式版 你是不是一脸懵逼(其实就是推断出来的)
Arrays.sort(rosterAsArray, Person::compareByAge);
Arrays.sort(rosterAsArray,Human::compareByAge);
for (int i = 0; i <rosterAsArray.length ; i++) {
System.out.println(rosterAsArray[i]);
}
// Arrays.stream(rosterAsArray).forEach((ro)->System.out.println(ro));
//Arrays.stream(rosterAsArray).forEach(System.out::println);
}
}
1. 引用静态方法
请注意,该接口Comparator
是功能接口。因此,您可以使用lambda
表达式,而不是定义并创建一个新类的实例,该实例实现Comparator
:
Arrays.sort(rosterAsArray,
(Person a, Person b) -> {
return a.getBirthday().compareTo(b.getBirthday());
}
);
但是,这种用于比较两个Person
实例的出生日期的方法已经存在Person.compareByAge
。您可以改为在lambda
表达式的主体中调用此方法:
Arrays.sort(rosterAsArray,
(a, b) -> Person.compareByAge(a, b)
);
由于此lambda
表达式会调用现有方法,因此您可以使用方法引用代替lambda
表达式:
Arrays.sort(rosterAsArray, Person::compareByAge);
方法引用Person::compareByAge
在语义上与lambda
表达式相同(a, b) -> Person.compareByAge(a, b)
。每个都有以下特征:
Comparator.compare
,这是(Person, Person)
。Person.compareByAge
。2. 引用特定对象的实例方法
class ComparisonProvider {
public int compareByName(Person a, Person b) {
return a.getName().compareTo(b.getName());
}
public int compareByAge(Person a, Person b) {
return a.getBirthday().compareTo(b.getBirthday());
}
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
方法引用myComparisonProvider::compareByName
调用compareByName
作为对象一部分的方法myComparisonProvider
。JRE
推断方法类型参数,在这种情况下为(Person
, Person)
。
3. 引用特定类型的任意对象的实例方法
String[] stringArray = { "Barbara", "James", "Mary", "John",
"Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
方法参考的等效lambda
表达式String::compareToIgnoreCase
将具有形式参数列表(String a, String b)
,其中a
和b
是用于更好地描述此示例的任意名称。方法引用将调用该方法a.compareToIgnoreCase(b)
。
4. 引用构造函数
功能接口Supplier
包含一个get
不带任何参数并返回一个对象的方法。因此,您可以transferElements
使用lambda
表达式调用该方法,如下所示:
Set<Person> rosterSetLambda =
transferElements(roster, () -> { return new HashSet<>(); });
您可以使用构造函数引用代替lambda
表达式,如下所示:
Set<Person> rosterSet = transferElements(roster, HashSet::new);
Java
编译器推断您要创建一个HashSet
包含type
元素的集合Person
。或者,您可以指定以下内容:
Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);
/**
* @author lane
* @date 2021年05月27日 下午6:29
*/
@FunctionalInterface
public interface MyColon {
int fiveTwoSeven();
}
/**
* @author lane
* @date 2021年05月28日 上午11:55
*/
public class MyColonImpl implements MyColon {
@Override
public int fiveTwoSeven() {
System.out.println("我是方式零:创建一个类MyColonImpl实现MyColon接口,昨天是5月27日");
return 0;
}
}
/**
* @author lane
* @date 2021年05月27日 下午6:34
*/
public class MyColonDemo {
public int fiveTwoSevenDemo() {
System.out.println("我是方式三:lambda推断类型II,也可以是方式四或者五");
return 0;
}
public static int staticFiveTwoSevenDemo(){
System.out.println("我是方式六:lambda推断类型II ::静态方法");
return 0;
}
}
真正的测试类
/**
* 举例子讲解::表达式的及其四种类型
* @author lane
* @date 2021年05月27日 下午6:24
*/
public class MyColonTest {
public static void main(String[] args) {
test1();
}
// 多种方式循序渐进方便理解
// test1引用特定对象的实例方法
public static void test1(){
//方式零:创建一个类MyColonImpl实现MyColon接口
System.out.println("=============方式零================");
MyColon myColon0 = new MyColonImpl();
myColon0.fiveTwoSeven();
//方式一:匿名内部类方式
System.out.println("=============方式一================");
MyColon myColon1 = new MyColon() {
@Override
public int fiveTwoSeven() {
System.out.println("我是方式一:匿名内容类方式,昨天是5月27");
return 0;
}
};
myColon1.fiveTwoSeven();
//方式二:lambda标准类型I
System.out.println("=============方式二================");
MyColon myColon2 = ()->{
System.out.println("我是方式二:lambda标准类型I,昨天是5月27");
return 0;};
myColon2.fiveTwoSeven();
//方式三:lambda推断类型II 方法都是返回int类型,可以推断出来
//注意:类MyColonDemo和接口MyColon 没有任何关系 方法名字不一样 但是参数和返回值类型一样
System.out.println("=============方式三================");
MyColonDemo myColonDemo = new MyColonDemo();
MyColon myColon3 = ()->{return myColonDemo.fiveTwoSevenDemo();};
myColon3.fiveTwoSeven();
//方式四:lambda推断类型II简化版
System.out.println("=============方式四================");
MyColon myColon4 = ()->myColonDemo.fiveTwoSevenDemo();
myColon4.fiveTwoSeven();
//方式五:lambda推断类型II ::表达式版 对象实例方法
System.out.println("=============方式五================");
MyColon myColon5= myColonDemo::fiveTwoSevenDemo;
myColon5.fiveTwoSeven();
//方式六: ::表达式版 类型静态方法
System.out.println("=============方式六================");
MyColon myColon6= MyColonDemo::staticFiveTwoSevenDemo;
myColon6.fiveTwoSeven();
//方式七: ::表达式版 引用特定类型的任意对象的实例方法
System.out.println("=============方式七================");
LongBinaryOperator longBinaryOperator = (long a, long b) -> Long.sum(a, b);
LongBinaryOperator longBinaryOperator2 = Long::sum;
System.out.println("方式七:引用特定类型如Long的sum方法"+longBinaryOperator2.applyAsLong(1, 2));
BiFunction<String, String, Boolean> endsWith = String::endsWith;
System.out.println("方式七:引用特定类型如String的endswith方法"+endsWith.apply("hello", "o"));
//引用String的排序方法
String[] stringArray = { "Barbara", "James", "Mary", "John",
"Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
Arrays.stream(stringArray).map(String::toUpperCase).toArray();
//方式八: ::表达式版 引用构造函数方法,一般还是那种特定类型
System.out.println("=============方式七================");
//无返回值的一般不用
/* public interface MyVoid {
void test();
}
MyVoid myvoid = MyColonDemo::new;*/
//返回值为该对象的
Callable<MyColonDemo> myColonDemoCall = MyColonDemo::new;
//返回值为hashSet
HashSet<String> hashSet = new HashSet<>();
Supplier<HashSet<String>> supHash = HashSet<String>::new;
//返回值为String的
Supplier<String> supplier = String::new;
}
}
测试结果
=============方式零================
我是方式零:创建一个类MyColonImpl实现MyColon接口,昨天是5月27日
=============方式一================
我是方式一:匿名内容类方式,昨天是5月27
=============方式二================
我是方式二:lambda标准类型I,昨天是5月27
=============方式三================
我是方式三:lambda推断类型II,也可以是方式四或者五
=============方式四================
我是方式三:lambda推断类型II,也可以是方式四或者五
=============方式五================
我是方式三:lambda推断类型II,也可以是方式四或者五
=============方式六================
我是方式六:lambda推断类型II ::静态方法
=============方式七================
方式七:引用特定类型如Long的sum方法3
方式七:引用特定类型如String的endswith方法true
=============方式七================
重点看下这段代码,类MyColonDemo和接口MyColon
//方式三:lambda推断类型II 方法都是返回int类型,可以推断出来
//注意:类MyColonDemo和接口MyColon 没有任何关系 方法名字不一样 但是参数和返回值类型一样
System.out.println("=============方式三================");
MyColonDemo myColonDemo = new MyColonDemo();
MyColon myColon3 = ()->{return myColonDemo.fiveTwoSevenDemo();};
myColon3.fiveTwoSeven();
//方式五:lambda推断类型II ::表达式版 对象实例方法
System.out.println("=============方式五================");
MyColon myColon5= myColonDemo::fiveTwoSevenDemo;
注意:
MyColon myColon3 = ()->{return myColonDemo.fiveTwoSevenDemo();};
简化后就是
MyColon myColon5 = myColonDemo::fiveTwoSevenDemo;
这种推断式写法,A接口 = B类::方法 的形式
个人看法
一般不建议使用,我们在写代码过程中难免会出现错误,这种形式对于看懂代码和寻找错误信息都是十分费劲的。毕竟能一眼看出来,谁也不喜欢推断,当然JRE是可以推断的啦!:: 表达式官方介绍翻译版可以看这篇文章JAVA 8 ‘::’ 关键字官方翻译版