import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
/**
* @author Kyle
* @version 1.0 2018年8月9日
* NEW java.util.function.Predicate<T>
* 接口定义了一个名叫test的抽象方法,它接受泛型
* T对象,并返回一个boolean。这恰恰和你先前创建的一样,现在就可以直接使用了。在你需要
* 表示一个涉及类型T的布尔表达式时,就可以使用这个接口。比如,你可以定义一个接受String 对象的Lambda表达式
* @function Predicate 谓词
*/
public class MyPredicate {
// jdk8自带的接口
// @FunctionalInterface
// public interface Predicate<T> {
// boolean test(T t);
// }
// 要筛选的list, p带筛选条件的对象,此处的p应该用Lambda(匿名函数)表达式来实现Predicate<T>接口
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> results = new ArrayList<>();
for (T s : list) {
if (p.test(s)) {
results.add(s);
}
}
return results;
}
public static void main(String[] args) {
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
// Predicate<String> nonEmptyStringPredicate 是接口定义
// (String s) -> !s.isEmpty(); 是对接口的实现,实现的方法是boolean test(T t); 返回一个boolean值
List<String> listOfStrings = new ArrayList<>();
listOfStrings.add(new String());
listOfStrings.add(new String("123"));
listOfStrings.add(new String("555"));
// 可以直接写实现
List<String> nonEmpty = filter(listOfStrings, (String s) -> s.isEmpty()); // 字符串是空的
System.out.println("空串元素的size: "+nonEmpty.size());
// 也可以用对象引用代替
List<String> nonEmpty2 = filter(listOfStrings, nonEmptyStringPredicate);
System.out.println("非空窜的size:"+nonEmpty2.size());
List<Apple> listApple = new ArrayList<>();
listApple.add(new Apple("green", 100));
listApple.add(new Apple("gray", 150));
listApple.add(new Apple("red", 200));
listApple.add(new Apple("red", 150));
listApple.add(new Apple("black", 50));
Predicate<Apple> applePredicate = (Apple a) -> a.getWeight() > 100 && "red".equals(a.getColor());
/* 请注意,当Lambda仅有一个类型需要推断的参数时,参数名称两边的括号也可以省略,
Lambda 类型推断
你还可以进一步简化你的代码。Java编译器会从上下文(目标类型)推断出用什么函数式接
口来配合Lambda表达式,这意味着它也可以推断出适合Lambda的签名,因为函数描述符可以通
过目标类型来得到。这样做的好处在于,编译器可以了解Lambda表达式的参数类型,这样就可
以在Lambda语法中省去标注参数类型。换句话说,Java编译器会像下面这样推断Lambda的参数
类型:①
List<Apple> greenApples = filter(inventory, a -> "green".equals(a.getColor()));
*/
// Predicate<Apple> applePredecate = a -> a.getWeight() > 100 && "red".equals(a.getColor());
// 请注意,当Lambda仅有一个类型需要推断的参数时,参数名称两边的括号也可以省略
// List<Apple> lsApple = filter(listApple, (Apple a) -> a.getWeight() > 100 && "red".equals(a.getColor())); // 直接写实现
List<Apple> lsApple = filter(listApple, applePredicate);
System.out.println("大于100的红色: "+lsApple.size());
/*
Lambda 对局部变量的限制,只能引用final 修饰的变量,或者隐式最终(隐式最终就是只能给变更赋值一次)
你可能会问自己,为什么局部变量有这些限制。第一,实例变量和局部变量背后的实现有一
个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局
部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线
程将这个变量收回之后,去访问该变量。因此,Java在访问自由局部变量时,实际上是在访问它
的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了
这个限制。
*/
// negate
Predicate<Apple> redApple = (Apple a) -> "red".equals(a.getColor());
List<Apple> lsRedApple = filter(listApple, redApple.negate()); // 非redApple.negate(),本来是比较苹果是红色的,加了就是negate()取返,相当于!=(不等)
System.out.println("非红色个数: "+lsRedApple.size());
// and 链接两个谓词来生成另一个Predicate对象
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150); // redApple = (Apple a) -> "red".equals(a.getColor()); 与的关系
lsRedApple = filter(listApple, redAndHeavyApple);
System.out.println("appple-red and getWeight() > 150 : "+lsRedApple.size());
// and or
Predicate<Apple> redAndHeavyAppleOrGreen =
redApple.and(a -> a.getWeight() > 150)
.or(a -> "green".equals(a.getColor())); // 一个参数可以省略(),参数的类型也可以通过Predicate<Apple>推导,所以只写了个a ->
lsRedApple = filter(listApple, redAndHeavyAppleOrGreen);
System.out.println("appple-red and getWeight() > 150 或者为绿色 : "+lsRedApple.size());
}
}
class Apple {
public int weight;
public String color;
public Apple(String color, int weight){
this.color = color;
this.weight = weight;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}