Java8新特性
- Lambda表达式
- 入门演示
-
- 案例1
-
- 如何解决 cannot be cast to java.lang.Comparable问题?
- 案例2
-
- 优化方式一 : 策略设计模式
- 优化方式二: 策略设计模式+匿名内部实现接口,减少创建实体类的麻烦
- 优化方式三: lambda表达式
- 优化方式四: stream流
- Lambda语法
-
- 语法格式一 : 无参数,无返回值
-
- 注意: 局部内部类与局部变量
- 语法格式二: 有一个参数,无返回值
- 语法格式三: 如果一个参数,那么小括号可以不写
- 语法格式四:有两个参数,有返回值,lambda体中有多条语句
- 语法格式五:若lambda体中只有一条语句,那么return和{}都可以省略不写
- 语法格式六: lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器可以通过上下文推断出数据类型,即“类型推断”
- Lambda需要函数式接口的支持
-
- 函数式接口: 接口中只有一个抽象方法的接口,称为函数式接口,可以使用@FunctionalIterface修饰,检查当前接口是否式函数式接口
- 我们可以通过lambda来实现函数式接口里面的唯一的抽象方法
- 举例一
-
- 涉及知识点一: Interage.compareTo()
- 涉及知识点二: Collections工具类
- 内置四大核心函数式接口
-
- Consumer :消费型接口
- Supplier:供给型接口
- Function
Lambda表达式
Lambda是一个匿名函数,可以理解为一段可以传递的代码(将代码像数据一样传递);可以写出更简洁、更灵活的代码;作为一种更紧凑的代码风格,是Java语言表达能力得到提升
入门演示
案例1
public class TestMain
{
//使用匿名内部类完成比较
@Test
public void test()
{
//比较器 匿名内部类,创建该接口的一个实现类
Comparator<People> com=new Comparator<People>() {
@Override
public int compare(People o1, People o2) {
return o1.getAge()-o2.getAge();
}
};
//TreeSet的特点是可排序、不重复
TreeSet<People> ts=new TreeSet<>(com);
ts.add(new People("大忽悠",20));
ts.add(new People("小忽悠",18));
ts.add(new People("大大大",22));
ts.add(new People("小朋友",17));
ts.forEach(System.out::println);
}
//lambda表达式替代匿名内部类
@Test
public void test1()
{
//比较器
Comparator<People> com=(p1,p2)-> p1.getAge()-p2.getAge();
//TreeSet的特点是可排序、不重复
TreeSet<People> ts=new TreeSet<>(com);
ts.add(new People("大忽悠",20));
ts.add(new People("小忽悠",18));
ts.add(new People("大大大",22));
ts.add(new People("小朋友",17));
ts.forEach(System.out::println);
}
}
如何解决 cannot be cast to java.lang.Comparable问题?
如何解决 cannot be cast to java.lang.Comparable问题?
案例2
要求对下面的代码进行优化:
public class TestMain
{
List<People> peopleList= Arrays.asList(
new People("1号",18,3000),
new People("2号",21,4000),
new People("3号",19,5000),
new People("4号",20,3500)
);
//获取年龄大于18的
public List<People> getAgeOver18()
{
List<People> list=new ArrayList<>();
for (People p:peopleList)
{
if(p.getAge()>18)
list.add(p);
}
return list;
}
//获取工资大于3000的
public List<People> getMoneyOver3000()
{
List<People> list=new ArrayList<>();
for (People p:peopleList)
{
if(p.getMoney()>3000)
list.add(p);
}
return list;
}
@Test
public void test()
{
List<People> ageOver18 = getAgeOver18();
ageOver18.forEach(System.out::println);
System.out.println("======================================");
List<People> moneyOver3000=getMoneyOver3000();
moneyOver3000.forEach(System.out::println);
}
}
优化方式一 : 策略设计模式
声明一个接口MyPrediect
public interface MyPrediect<T>
{
public boolean test(T t);
}
接口的实现类一FilterPeoAge,负责过滤年龄:
public class FilterPeoAge implements MyPrediect<People>{
@Override
public boolean test(People people) {
return people.getAge()>18;
}
}
接口实现类二FilterPeoMoney,负责过滤金钱
public class FilterPeoMoney implements MyPrediect<People>{
@Override
public boolean test(People people) {
return people.getMoney()>3000;
}
}
测试演示:
public class TestMain
{
List<People> peopleList= Arrays.asList(
new People("1号",18,3000),
new People("2号",21,4000),
new People("3号",19,5000),
new People("4号",20,3500)
);
public List<People> FilterPeo(List<People> list,MyPrediect<People> mp)
{
List<People> peopleList=new ArrayList<>();
for (People p:list)
{
if(mp.test(p))
peopleList.add(p);
}
return peopleList;
}
@Test
public void test()
{
List<People> peopleList = FilterPeo(this.peopleList, new FilterPeoAge());
peopleList.forEach(System.out::println);
System.out.println("===========================");
List<People> peopleList1 = FilterPeo(peopleList, new FilterPeoMoney());
peopleList1.forEach(System.out::println);
}
}
当我们还需要安装某个策略进行过滤时,只需要实现接口,完成相应策略过滤逻辑编写即可
优化方式二: 策略设计模式+匿名内部实现接口,减少创建实体类的麻烦
public class TestMain
{
List<People> peopleList= Arrays.asList(
new People("1号",18,3000),
new People("2号",21,4000),
new People("3号",19,5000),
new People("4号",20,3500)
);
public List<People> FilterPeo(List<People> list,MyPrediect<People> mp)
{
List<People> peopleList=new ArrayList<>();
for (People p:list)
{
if(mp.test(p))
peopleList.add(p);
}
return peopleList;
}
@Test
public void test()
{
List<People> peopleList = FilterPeo(this.peopleList, new MyPrediect<People>() {
@Override
public boolean test(People people) {
return people.getAge()>18;
}
});
peopleList.forEach(System.out::println);
System.out.println("===========================");
List<People> peopleList1 = FilterPeo(peopleList, new MyPrediect<People>() {
@Override
public boolean test(People people) {
return people.getMoney()>3000;
}
});
peopleList1.forEach(System.out::println);
}
}
优化方式三: lambda表达式
public class TestMain
{
List<People> peopleList= Arrays.asList(
new People("1号",18,3000),
new People("2号",21,4000),
new People("3号",19,5000),
new People("4号",20,3500)
);
public List<People> FilterPeo(List<People> list,MyPrediect<People> mp)
{
List<People> peopleList=new ArrayList<>();
for (People p:list)
{
if(mp.test(p))
peopleList.add(p);
}
return peopleList;
}
@Test
public void test()
{
List<People> peopleList = FilterPeo(this.peopleList, (people)-> people.getMoney()>4000);
peopleList.forEach(System.out::println);
}
}
优化方式四: stream流
public class TestMain
{
List<People> peopleList= Arrays.asList(
new People("1号",18,3000),
new People("2号",21,4000),
new People("3号",19,5000),
new People("4号",20,3500)
);
public List<People> FilterPeo(List<People> list,MyPrediect<People> mp)
{
List<People> peopleList=new ArrayList<>();
for (People p:list)
{
if(mp.test(p))
peopleList.add(p);
}
return peopleList;
}
@Test
public void test()
{
peopleList.stream().filter(people -> people.getMoney()>3000).limit(2).forEach(System.out::println);
}
}
Lambda语法
- 操作符:->
- 左侧:参数列表
- 右侧:执行代码块 / Lambda 体
即lambda是对接口的抽象方法的实现,可能有人会问,如果接口中抽象方法存在多个,那lambda是对哪个抽象方法的实现呢?
其实lambda需要一个函数式接口的支持,即当前接口只有一个抽象方法
语法格式一 : 无参数,无返回值
@Test
public void test()
{
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("方法执行中.....");
}
};
runnable.run();
System.out.println("----------------------");
Runnable r1=()-> System.out.println("r1执行中....");
r1.run();
}
注意: 局部内部类与局部变量
局部内部类在JDK8之前只能使用成员变量和被final修饰的局部变量。JDK8之后,局部内部类如果使用局部变量那么局部变量默认被final修饰,但如果局部变量被重新赋值,那么局部内部类将不能在使用。具体看下例子:
public void show() {
int j = 0; //jdk1.8后默认被final修饰。1.8之前局部内部类只能访问成员变量,和被final修饰的局部变量
class A{
public void showA() {
System.out.println(i);
System.out.println(j);
}
}
// j = 2;//如果对局部变量做了修改,则默认会变成不被final修饰。局部内部类不可调用
}
//注:有静态成员的一定是静态内部类
lambda这里跟匿名内部类用法一致
语法格式二: 有一个参数,无返回值
public class TestMain
{
@Test
public void test()
{
Consumer<String> consumer=(x)-> System.out.println(x);
// void accept(T t); 对该抽象方法进行实现
consumer.accept("大忽悠");
}
}
语法格式三: 如果一个参数,那么小括号可以不写
Consumer<String> consumer=x-> System.out.println(x);
// void accept(T t); 对该抽象方法进行实现
consumer.accept("大忽悠");
语法格式四:有两个参数,有返回值,lambda体中有多条语句
public class TestMain
{
@Test
public void test()
{
// int compare(T o1, T o2);重写该方法
Comparator<Integer> com=(x,y)->{
System.out.println("函数式接口");
return Integer.compare(x,y);
};
int compare = com.compare(1, 2);
System.out.println(compare);
}
}
语法格式五:若lambda体中只有一条语句,那么return和{}都可以省略不写
public void test()
{
// int compare(T o1, T o2);重写该方法
Comparator<Integer> com=(x,y)->Integer.compare(x,y);
int compare = com.compare(1, 2);
System.out.println(compare);
}
语法格式六: lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器可以通过上下文推断出数据类型,即“类型推断”
Lambda需要函数式接口的支持
函数式接口: 接口中只有一个抽象方法的接口,称为函数式接口,可以使用@FunctionalIterface修饰,检查当前接口是否式函数式接口
我们可以通过lambda来实现函数式接口里面的唯一的抽象方法
举例一
public class TestMain
{
List<People> peopleList= Arrays.asList(
new People("1号",18,3000),
new People("2号",20,4000),
new People("3号",18,5000),
new People("4号",20,3500)
);
//先安装年龄升序排序,年龄相同,再安装money降序排序
@Test
void test()
{
//重写下面这个方法,比较器可以为空
// public static <T> void sort(List<T> list, Comparator<? super T> c) {
// list.sort(c);
// }
Collections.sort(peopleList,(p1,p2)->{
if(p1.getAge()==p2.getAge())
return -p1.getMoney().compareTo(p2.getMoney());
else
return p1.getAge().compareTo(p2.getAge());
});
peopleList.forEach(System.out::println);
}
}
涉及知识点一: Interage.compareTo()
Integer obj2=100;
//比较大小
System.out.println(obj2.compareTo(100));
System.out.println(obj2.compareTo(102));
System.out.println(obj2.compareTo(10));
结果: 0 , -1 , 1 ,即大于返回 1 ,小于返回 -1 ,等于返回 0
涉及知识点二: Collections工具类
详细用法
内置四大核心函数式接口
Consumer<T> :消费型接口
void accept(T t);
Supplier<T>:供给型接口
T get(T t);
Function<T,R>:函数型接口
R apply(T t);
Predicate<T>:断言型接口
boolean test(T t);
Consumer :消费型接口
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
}
测试:
public class TestMain
{
@Test
void test()
{
//这里的lambda表达式实现了对应的抽象函数
happy(10000,(m)-> System.out.println("我有: "+m));
}
public void happy(Integer money,Consumer<Integer> com)
{
com.accept(money);
}
}
Supplier:供给型接口
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
测试:
public class TestMain
{
@Test
void test()
{
List<String> numList=getNumList(5,()->UUID.randomUUID().toString().substring(0,8));
numList.forEach(System.out::println);
}
//需求: 产生指定个数的随机id,并放入集合中
public List<String> getNumList(int num, Supplier<String> supplier)
{
ArrayList<String> integers = new ArrayList<String>();
for(int i=0;i<num;i++)
{
integers.add(supplier.get());
}
return integers;
}
}
Function<T,R>: 函数型接口
public interface Function<T, R>{
R apply(T t);
}
测试:
public class TestMain
{
@Test
void test()
{
String str=StrHandler("\t\t\t 大忽悠 \t\t\t",(s)->s.trim());
System.out.println(str);
}
//需求: 处理字符串
public String StrHandler(String str, Function<String,String> f)
{
return f.apply(str);//返回被处理过后的字符串
}
}
涉及知识点: trim()函数
trim()方法的使用
Predicate:断言型接口
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
}
测试:
public class TestMain
{
@Test
void test()
{
StrHandler("大忽悠啊",(str)->str.length()>3);
StrHandler("嘿嘿",(str)->str.length()>3);
}
//需求: 处理字符串
public void StrHandler(String str, Predicate<String> predicate)
{
if(predicate.test(str))
System.out.println(str);
else
System.out.println("太短了");
}
}