《Java编程的逻辑》笔记
概念和原理
-
泛型:泛型就是类型参数化,处理的数据类型不是固定的,而是可以作为参数传入。如下
- 泛型类:T表示类型参数。
public class Pair<T>{ T first; T second; public Pair(T first,T second){ this.first = first; this.second = second; } public T getFirst(){ return first; } public T getSecond(){ return second; } } 复制代码
-
泛型方法:
一个方法是不是泛型的,与它所在的类是不是泛型没有是什么关系。
public static <T> int indexoOf(T[] arr,T elm){ for(int i=0;i<arr.length;i++){ if(arr[i].equale(elm)){ return i; } } return -1; } // 泛型参数可以是多个 public static <U,V> Pair<U,V> makePair(U first,V second){ Pair<U,V> pair = new Pair<>(first,second); return pair; } // 泛型方法调用时一般不需要特意指定类型参数的实际类型 makePair(1,"老马"); 复制代码
-
泛型接口:
// 泛型接口 public interface Comparable<T> { public int compareTo(T o); } // 实现 public final class Integer extends Number implements Comparable<Integer>{ public int compareTo(Integer anotherInteger){ return compare(this.value,anotherInteger.value); } } 复制代码
-
基本原理:Java泛型是通过擦除实现的,类定义中的类型参数如T会被替换为Object,在程序运行过程中,不知道泛型的实际类型参数,比如Pair,运行中只知道Pair,而不知道Integer。
-
泛型的好处:
- 更好的安全性
- 更好的可读性
类型参数的限定
-
限定上界:现在我们知道Java把类型参数,当做Object,但Java支持限定这个参数的上界。参数必须为给定的上界类型或其子类型,这个限定是通过extends关键字类表示的。这个上界可以是某个具体的类或某个具体接口,也可以是其他类型参数。
-
上界为具体类:
public class NumberPair<U extends Number,V extends Number> extends Pair<U,V>{ public NumberPair(U first,V second){ super(first,second); } } //调用 NumberPair<Integer,Double> pair = new NumberPair<>(10,12.34); 复制代码
-
上界为某个接口:
// T表示一种数据类型,必须实现Comparable接口,且必须可以与相同类型的元素进行比较 public static <T extends Comparable<T>> T max(T[] arr){ T max = arr[0]; for(int i=1;i<arr.length;i++){ if(arr[i].compareTo(max)>0){ max = arr[i]; } } return max; } 复制代码
-
上界为其他类型参数:
//E是DynamicArray的类型参数,T是addAll的类型参数,T的上界限定为E public <T extends E> void addAll(DynamicArray<T> c){ for(int i= 0;i<c.length;i++){ add(c.get(i)); } } DynamicArray<Number> numbers = new DynamicArray<>(); DynamicArray<Integer> ints = new DynamicArray<>(); ints.add(100); ints.add(43); numbers.addAll(ints); 复制代码
解析通配符
- 通配符: <? extends E>
- 无限定通配符:, DynamicArray<?> 这种限制:只能读,不能写
- 超类型通配符:<? super E>
- 通配符比较:
- 它们的目的都是为了使方法接口更为灵活,可以接受更为广泛的类型。
<? super E>
用于灵活写入或比较,使得对象可以写入父类型的容器,使得父类型的比较方法可以应用于子类型对象,它不能被类型参数形式替代。<?>和<? extends E>
用于灵活的读取,使得方法可以读取E或E的任意子类型的容器对象,它们可以用类型参数形式替代,但通配符形式更为简洁。
细节和局限性
- 使用泛型类、方法和接口 注意点:
- 基本类型不能用于实例化类型参数
- 运行时类型信息不适用于泛型
- 类型擦除可能会引发一些冲突
- 定义泛型类,方法和接口 注意点:
- 不能通过类型参数创建对象
- 泛型类类型参数不能用于静态变量和方法
- 了解多个类型限定的语法
- 泛型与数组
- 不能创建泛型数组
- Java不支持泛型数组
- 如果需要存放反省对象,可以使用原始类型的数组,或者使用泛型容器
- 泛型容器内部使用Object数组,如果要转换泛型容器为对应类型的数组,需要使用反射。