Java8特性二


题外话

泛型<? super T><? extends T>

class Food {}
class Fruit extends Food {}
class Apple extends Fruit {}
class Banana extends Fruit {}
class Meat extends Food {}
class RedApple extends Apple {}
class GreenApple extends Apple {}

class Plate<T> {
    private T item;
    public Plate(T t){item = t;}

    public T get() {
        return item;
    }

    public void set(T item) {
        this.item = item;
    }
}

继承关系

image-20210923100802232

新建一个盘子,set会报错。

image-20210923095453887

编译器只知道容器内是Fruit或者它的派生类,但具体是什么类型不知道。所以取的时候要用Fruit或它的基类。

image-20210923100342048

<? extends Fruit>会使往盘子里放东西的set( )方法失效。但取东西get( )方法还有效(用Fruit或它的基类)。

使用下界<? super Fruit>会使从盘子里取东西的get( )方法部分失效,只能存放到Object对象里。set( )方法正常。

Plate<? super Apple> p = new Plate<Fruit>(new Fruit());
Fruit f = p.get(); //error, super所以不确定哪个类能放下,要用Object
Object apple = p.get(); // ok
p.set(new Apple());
p.set(new GreenApple());
p.set(new Fruit()); //error, p放的是apple的超类,所以apple及以下肯定能放,以上不确定

PECS原则 Producer Extends Consumer Super。(相对外界就是producer)频繁往外读取的,适合用上界Extends。经常插入的,适合用下界Super。

Java 8

foreach

List<Integer> list = new LinkedList<>();
list.add(3);
list.add(1);
list.add(2);

for(Integer i:list) {
    System.out.println(i);
}

System.out.println("=================");

list.forEach(System.out::println); // 匿名类,lambda,方法引用
//输出:
//3
//1
//2
//=================
//3
//1
//2

数组相关

//迭代函数,生成有序无限的数据流。
IntStream.iterate(2,n->n*3).limit(10).forEach(System.out::println);
//2
//6
//18
//54
//162
//486
//1458
//4374
//13122
//39366

将数组分割成指定大小的小数组

public class Demo {
    public static int[][] chunk(int[] numbers,int size) {
        return IntStream.iterate(0,i->i+size) //0 3 6
                .limit((long)Math.ceil((double) numbers.length/size))
                .mapToObj(cur -> Arrays.copyOfRange(numbers, cur, // 0-> [1,4,5], 3->[7,8,9],6->[4,7]
                        cur + size > numbers.length ? numbers.length : cur + size))
                .toArray(int[][]::new);
    }
    public static void main(String[] args) {
        int[] numbers = {1,4,5,7,8,9,4,7};
        System.out.println(Arrays.deepToString(chunk(numbers, 3)));
        //[[1, 4, 5], [7, 8, 9], [4, 7]]
    }
}

concat

强行类型转换没有检查:

public class Demo {
    public static <T> T[] concat(T[] first,T[] second) {
        return (T[]) Stream.concat(
                Stream.of(first),
                Stream.of(second)
        ).toArray();
    }
    public static void main(String[] args) {
        Integer[] numbers = {1,4,5,7,8,9,4,7};
        String[] strings = {"12e3","456"};
        System.out.println(Arrays.toString(concat(numbers, strings)));
        //[1, 4, 5, 7, 8, 9, 4, 7, 12e3, 456]
    }
}

有检查:

public class Demo {
    public static <T> T[] concat(T[] first,T[] second) {
        return Stream.concat(
                Stream.of(first),
                Stream.of(second)
        ).toArray(i -> (T[]) Arrays.copyOf(new Object[0], i, first.getClass()));
        // 创建一个Object类型的数组,大小为0,Arrays.copyOf(new Object[0], i, first.getClass())
        // 返回一个T[10]的全空对象
        // 用法:toArray(size -> new String[size])
        // 正如上所示,是对的
    }
    public static void main(String[] args) {
        Integer[] numbers = {1,4,5,7,8,9,4,7};
        String[] strings = {"12e3","456"};
        System.out.println(Arrays.toString(concat(numbers, strings))); // ArrayStoreException
    }
}

image-20210923115400216

统计值出现次数

public class Demo {
    public static long countOccurrences(int[] numbers, int value) {
        return Arrays.stream(numbers)
                .filter(i->i==value)
                .count();
    }
    public static void main(String[] args) {
        int[] numbers = {1,4,5,7,8,9,4,7};
        System.out.println(countOccurrences(numbers,4));
    }
}

还有很多小例子,慢慢看吧: biezhi/30-seconds-of-java8

参考

Java 泛型 <? super T> 中 super 怎么 理解?与 extends 有何不同? - 胖君的回答 - 知乎

Java8 forEach 指南

biezhi/30-seconds-of-java8