持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
1、Interface
interface 的设计初衷是面向抽象,提高扩展性。这也留有一点遗憾,Interface修改的时候,实现它的类也必须跟着修改。为了解决接口的修改与现有实现不兼容的问题。新 interface的方法可以用 default 或 static 修饰,这样就可以有方法体,实现类也不必重写此方法。
一个 interface 中可以有多个方法被它们修饰,这 2 个修饰符的区别主要也是普通方法和静态方法的区别。
- default 修饰的方法,是普通实例方法,可以用this调用,可以被子类继承、重写。
- static 修饰的方法,使用上和一般类静态方法一样。但它不能被子类继承,只能用Interface调用。
public interface InterfaceNew1 {
void f();
static void sm(){
System.out.println("interface 1 static 方法实现");
}
default void def() {
System.out.println("interface 2 default 方法实现");
}
}
public interface InterfaceNew2 {
default void def() {
System.out.println("interface 2 default 方法实现");
}
}
public class InterfaceTest implements InterfaceNew1,InterfaceNew2 {
@Override public void f() {
}
@Override public void def() {
InterfaceNew1.super.def();
}
public static void main(String[] args) {
InterfaceTest test = new InterfaceTest();
test.def();
}
}
注意事项:
如果有一个类既实现了 InterfaceNew 接口又实现了 InterfaceNew1接口,它们都有def(),并且 InterfaceNew 接口和 InterfaceNew1接口没有继承关系的话,这时就必须重写def()。不然的话,编译的时候就会报错。
2、Functional Interface 函数式接口
函数式接口,也称 SAM 接口,即 Single Abstract Method interfaces (单个抽象方法接口),有且只有一个抽象方法,但可以有多个非抽象方法的接口。
在 java 8 中专门有一个包放函数式接口java.util.function,该包下的所有接口都有@FunctionalInterface 注解,提供函数式编程。 在其他包中也有函数式接口,其中一些没有@FunctionalInterface 注解,但是只要符合函数式接口的定义就是函数式接口,与是否有 @FunctionalInterface注解无关,注解只是在编译时起到强制规范定义的作用。其在 Lambda 表达式中有广泛的应用。
2.1 @FunctionalInterface 注解
如果一个类型被这个注解修饰,那么必须满足如下的条件才能编译通过:
-
这个类型必须是一个 interface 接口;
-
接口有且只有一个抽象方法,但可以用多个非抽象方法;
2.2 自定义函数接口
@FunctionalInterface
public interface MyFunctionInterface {
void study();
}
public class Main {
public static void main(String[] args) {
MyFunctionInterface myFunctionInterface = () -> System.out.println("Hello Function Interface!");
myFunctionInterface.study();
}
}
2.3 内置四大函数接口
2.3.1 消费型接口
Consumer 执传入一个T类型的参数,执行一段处理函数,无返回结果。
@Test
public void consumerTest() throws Exception {
Consumer<String> consumer = s -> System.out.println("Consumer:"+s);
consumer.accept("consumer");
}
2.3.2 供给型接口
Suppiler 执行一段处理函数,无输入,返回一个T类型的结果。
/**
* 供给型
* @throws Exception
*/
@Test
public void supplierTest() throws Exception {
//Supplier<String> supplier = () -> {return "Supplier";};
Supplier<String> supplier = () -> "Supplier";
System.out.println(supplier.get());
}
2.3.3 断定型接口
Predicate 传入一个T类型的参数,执行一段处理函数,返回一个boolean型的结果。
/**
* 断定型
* @throws Exception
*/
@Test
public void predicateTest() throws Exception {
//Predicate<String> predicate = s -> "Predicate".equals(s);
Predicate<String> predicate = "Predicate"::equals;
System.out.println(predicate.test("A"));
}
2.3.4 函数型接口
Function 传入一个T类型的参数,执行一段处理函数,返回一个R类型的结果。
/**
* 函数型
* @throws Exception
*/
@Test
public void functionTest() throws Exception {
Function<String,String> function = str -> "Function:"+str;
System.out.println(function.apply("Hello"));
}
3、Lambda表达式
Lambda表达式是一个匿名的函数,Java 8允许把函数作为参数传递进方法中。使用Lambda表达式可以使代码变的更加简洁紧凑。让Java也能支持简单的函数式编程。
3.1 语法格式
(parameters) -> expression 或 (parameters) ->{ statements; }
3.2 Lambda 应用
3.2.1 替代匿名内部类
1)Runable接口
@Test
public void runnableTest() throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("The runnable now is using!");
}
}).start();
new Thread(() -> System.out.println("It's a lambda function!")).start();
}
2)Comparator接口
@Test
public void comparatorTest() throws Exception {
List<Integer> integers = Arrays.asList(1, 2, 3);
Collections.sort(integers, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
});
//lambda
Collections.sort(integers,((o1, o2) -> o1 - o2));
// 分解
Comparator<Integer> comparator = ((o1, o2) -> o1- o2);
Collections.sort(integers,comparator);
}
3.2.2 集合迭代
@Test
public void lambdaFor () throws Exception {
List<String> strings = Arrays.asList("1","2","3");
//传统foreach
for (String s: strings) {
System.out.println(s);
}
//Lambda foreach
strings.forEach((s) -> System.out.println(s));
strings.forEach(System.out::println);
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"Hello");
map.put(2,"Lambda");
map.put(3,"Opm");
map.forEach((k,v) -> System.out.println(v));
}
3.2.3 方法的应用
Java 8允许使用 :: 关键字来传递方法或者构造函数引用,无论如何,表达式返回的类型必须是 function-interface。
public class LambdaClassSuper {
LambdaInterface sf(){
return null;
}
}
public class LambdaClass extends LambdaClassSuper {
public static LambdaInterface staticF() {
return null;
}
public LambdaInterface f() {
return null;
}
void show() {
//1.调用静态函数,返回类型必须是functional-interface
LambdaInterface t = LambdaClass::staticF;
//2.实例方法调用
LambdaClass lambdaClass = new LambdaClass();
LambdaInterface lambdaInterface = lambdaClass::f;
//3.超类上的方法调用
LambdaInterface superf = super::sf;
//4. 构造方法调用
LambdaInterface tt = LambdaClassSuper::new;
}
}
3.2.4 访问变量
lambda 表达式可以引用外边的变量,但是该变量默认拥有final属性,不能被修改,如果修改,编译时就报错。
int i = 0; Collections.sort(strings, (Integer o1, Integer o2) -> o1 - i);
4、Stream
4.1 概念
Java 新增了 java.util.stream 包,它和之前的流大同小异。之前接触最多的是资源流,比如java.io.FileInputStream,通过流把文件从一个地方输入到另一个地方,它只是内容搬运工,对文件内容不做任何CRUD。 Stream依然不存储数据,不同的是它可以检索(Retrieve)和逻辑处理集合数据、包括筛选、排序、统计、计数等。可以想象成是 Sql 语句。 它的源数据可以是 Collection、Array 等。由于它的方法参数都是函数式接口类型,所以一般和 Lambda 配合使用。
Stream流分为 stream 串行流和 parallelStream 并行流。并行流可多线程执行。
4.2 常用方法
java.util.stream.Stream
/** * 返回一个串行流 */
default Stream<E> stream()
/** * 返回一个并行流 */
default Stream<E> parallelStream()
/** * 返回T的流 */
public static<T> Stream<T> of(T t)
/** * 返回其元素是指定值的顺序流。 */
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
/** * 过滤,返回由与给定predicate匹配的该流的元素组成的流 */
Stream<T> filter(Predicate<? super T> predicate);
/** * 此流的所有元素是否与提供的predicate匹配。 */
boolean allMatch(Predicate<? super T> predicate)
/** * 此流任意元素是否有与提供的predicate匹配。 */
boolean anyMatch(Predicate<? super T> predicate);
/** * 返回一个 Stream的构建器。 */
public static<T> Builder<T> builder();
/** * 使用 Collector对此流的元素进行归纳 */
<R, A> R collect(Collector<? super T, A, R> collector);
/** * 返回此流中的元素数。 */
long count();
/** * 返回由该流的不同元素(根据 Object.equals(Object) )组成的流。 */
Stream<T> distinct();
/** * 遍历 */
void forEach(Consumer<? super T> action);
/** * 用于获取指定数量的流,截短长度不能超过 maxSize 。 */
Stream<T> limit(long maxSize);
/** * 用于映射每个元素到对应的结果 */
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
/** * 根据提供的 Comparator进行排序。 */
Stream<T> sorted(Comparator<? super T> comparator);
/** * 在丢弃流的第一个 n元素后,返回由该流的 n元素组成的流。 */
Stream<T> skip(long n);
/** * 返回一个包含此流的元素的数组。 */
Object[] toArray();
/** * 使用提供的 generator函数返回一个包含此流的元素的数组,以分配返回的数组,以及分区执行或调整大小可能需要的任何其他数组。 */
<A> A[] toArray(IntFunction<A[]> generator);
/** * 合并流 */
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
4.3 使用
@Test
public void streamApi() throws Exception {
List<String> strings = Arrays.asList("Hell", "Zero", "Open","");
// 返回符合条件的Stream
Stream<String> stringStream = strings.stream().filter(s -> !"".equals(s));
System.out.println("满足条件的数量:"+stringStream.count());
//遍历打印元素
strings.stream().forEach(System.out::println);
//limit 获取到一个元素的stream
Stream<String> limit = strings.stream().limit(1);
String[] array = limit.toArray(String[]::new);
System.out.println(array[0]);
//map 对每个元素操作,返回新的流
Stream<String> map = strings.stream().map(s -> "[" + s + "]");
//sorted 排序并打印
map.sorted().forEach(s -> System.out.print(s));
// collect
List<String> collect = strings.stream().filter("Open"::equals).collect(Collectors.toList());
System.out.println(collect);
//把list转为string 各个元素用,号隔开
String str = strings.stream().filter(s -> !s.isEmpty()).collect(Collectors.joining(","));
System.out.println(str);
// 数组的统计
List<Integer> number = Arrays.asList(1,2,5,4);
// int型 摘要统计
IntSummaryStatistics intSummaryStatistics = number.stream().mapToInt(x -> x).summaryStatistics();
System.out.println("列表中最大的数:"+intSummaryStatistics.getMax());
System.out.println("列表中最小的数:"+intSummaryStatistics.getMin());
System.out.println("平均数:"+intSummaryStatistics.getAverage());
System.out.println("所有数之和:"+intSummaryStatistics.getSum());
// concat 合并流
List<String> strings2 = Arrays.asList("Tom","Jack");
long count = Stream.concat(strings.stream(), strings2.stream()).count();
System.out.println("流合并后元素的数量:"+count);
}
@Test
public void streamApiError() throws Exception {
/**
* TODO: 一个stream只能操作一次,不能断开,否则会报错
* java.lang.IllegalStateException: stream has already been operated upon or closed
*/
List<String> strings = Arrays.asList("Hell", "Zero", "Open");
Stream<String> stream = strings.stream();
// 第一次使用后流已关闭
//stream.limit(2);
// 第二次使用 报错
//stream.forEach(System.out::println);
stream.limit(2).forEach(System.out::println);
}
4.4 延迟执行
在执行返回 Stream 的方法时,并不立刻执行,而是等返回一个非 Stream 的方法后才执行。因为拿到 Stream 并不能直接用,而是需要处理成一个常规类型。这里的 Stream 可以想象成是二进制流。
@Test
public void laziness() throws Exception {
List<String> strings = Arrays.asList("Hell", "Zero", "Open");
Stream<String> stream = strings.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
System.out.println("Predicate Run");
return true;
}
});
System.out.println("count 执行!");
stream.count();
}
结果:
count 执行!
Predicate Run
Predicate Run
Predicate Run