Tong's Digital Garden

Logo

welcome to Tong's Digital Garden

 bigguaiwutong@qq.com

View My GitHub Profile

Ref:Lesson: Generics

泛型的好处

泛型类型命名规范

Parameter VS argument Parameter: 声明的参数类型 Argument: 实际传入的参数 当调用一个方法时,我们传入的 argument 必须符合 parameter 的类型和顺序

<>

多个类型参数 参数化类型

原始类型 (raw types)

前置信息:我们有一个如下的泛型类

public class Box<T> {
	//...
}

什么是 raw types

为了创建一个参数化的 Box<T>,需要为形式类型参数(parameter)提供一个真正的参数(argument),如果省略了实际参数(argument),那么就是原始类型(raw types),非泛型类或接口没有 raw types

//etc
Box box = new Box();

在使用 raw types 时会绕过泛型类型检查,将不安全代码的捕获推迟到运行时,因此,我们要尽量避免使用 raw types

unchecked error

当将一个 raw types 的泛型类赋值给一个具体的泛型类时就会发生 unchecked error

//etc
Box<Integer> bi;
bi = new Box();
//将会提示如下 msg
warning:[unchecked] unchecked conversion

解决方案:

  1. 使用  -Xlint:-unchecked 重新进行编译
  2. 使用注解 @SuppressWarnings("unchecked")

泛型方法

泛型方法时引入自己的类型参数(parameters)的方法

在泛型方法语法中,在返回值中使用到的 type,需要在方法的入参中被定义(放在尖括号中),对于静态方法,类型参数必须出现在返回类型之前

//etc
public class Util {
	public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
		return p1.getKey().equals(p2.getKey);
	}
}

public class Pair<K, V> {
	private K key;
	private V value;
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

//对 compare 方法的使用
Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "banana");
boolean same = Util.<Integer, String>compare(p1, p2); 
// 上一行 <Integer, String> 的声明可以去除,因为可以通过 type inference 进行自动匹配
//上一行可以简化为
boolean same = Util.compare(p1, p2); 

有界类型参数(parameters)

  1. 通过有界类型参数我们可以限制传入的 parameter 的类型,例如:我的计算工具类只想接受 Integer 或其子类
//etc
public <U extends Integer> void inspect(U u) {
	...
}

通过这种方式,传入不被允许的类型时,编译就会报错

  1. 除此以外,这也将允许我们调用边界中定义的方法
//etc
public class NatualNumber<T extends Integer> {
	privaye T n;
	public NaturalNumber(T n)  { this.n = n; }
	public boolean isEven() {
		return n.intValue % 2 == 0;   
	}
}
  1. 边界也可以有多个
public class NatualNumber<T extends A & B & C> {
	...
}
  1. 泛型方法和有界类型参数
    public static <T> int countGreaterThan(T[] anArray, T elem) {
     int count = 0;
     for (T e : anArray)
         if (e > elem)  // compiler error
             ++count;
     return count;
    }
    

这里编译失败的原因时因为 > 只适用于 short, int 等类型,但是我们可以通过限制 Comparable<T> 来实现

public interface Comparable<T> {
    Public int compareTo (T o);
}
public static <T extends Comparable<T>> int countGreaterThan (T[] anArray, T elem) {
    Int count = 0;
    For (T e : anArray)
        if (e.compareTo (elem) > 0)
            ++count;
    Return count;
}

泛型与继承

image.png

通配符

在泛型代码中,? 被称为通配符,用于表示未知类型,可以用于 parameter 但是不能用于 argument

public class A {
	...
}
public class B extends A {
	...
}
public class C extends A{
	...
}

上界通配符

上界通配符可以放宽对变量的限制,使用 extends

public static void process(List<? extends A> list) {
	//...
}

上面可以接受匹配 A 和 A 的任意子类型(A,B,C 类均可)

无界通配符

无界通配符使用 ? 进行指定,表示未知类型的列表。 Class<?> 经常会被分的 Class<T> 方法并依赖于 `T

下界通配符

将未知类型限制为特定类型或者该类型的父类型,使用

java public static void process(List<? super B> list) { //... }

可以接受的类型为 B 和 A

通配符的使用

假设一个方法 copy(src, dest) src 提供需要复制的数据,是 in 变量, dest 参数接受数据,是 out 变量

  1. in 变量通常使用上界通配符进行定义
  2. out 变量通常使用下界通配符进行定义
  3. in 变量可以通过 Object 类中定义的方法访问,使用无界通配符
  4. 如果代码需要将变量作为 in 和 out 变量访问,不要使用通配符
  5. 返回类型应尽量避免使用通配符

back

home