Java

java 泛型

2021-09-08  本文已影响0人  zhbi98

1. 泛型的优点

在没有泛型的情况的下,通过对类型 Object 的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是本身就是一个安全隐患。那么泛型的好处就是在编译的时候能够检查类型安全,并且所有的强制转换都是自动和隐式的。

使用泛型的例子:使用泛型指定数据类型和强制转换的比较,如下代码

public class Generic<T> {
    private T t;

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

    public T get() {
        return t;
    }
}


public class GenericLearn {
    // 不指定类型
    public void noSpecifyType() {
        Generic generic = new Generic();
        generic.set("test");

        // 需要使用强制类型转换
        String test = (String) generic.get();
        System.out.println(test);
    }

    // 这里使用泛型指定类型,指定类型为String
    public void specifyType() {
        Generic<String> generic = new Generic<>();
        generic.set("test");

        // 不需要使用强制类型转换
        String test = generic.get();
        System.out.println(test);
    }

    public static void main(String[] args) {

    }
}

2. 使用泛型

1. 在这里我们使用java 的可变长数组ArrayList作为例子说明泛型。

使用ArrayList时,如果不定义泛型类型时,泛型类型实际上就是Object,此时,只能把<T>当作Object使用,没有发挥泛型的优势。

// 编译器发生警告:
List list = new ArrayList();
list.add("Hello");
list.add("World");
String first = (String) list.get(0);
String second = (String) list.get(1);

当我们定义泛型类型<String>后,List<T>的泛型接口变为强类型List<String>。

// 无编译器警告:
List<String> list = new ArrayList<String>();
list.add("Hello");
list.add("World");
// 无强制转型:
String first = list.get(0);
String second = list.get(1);

当我们定义泛型类型<Number>后,List<T>的泛型接口变为强类型List<Number>。

List<Number> list = new ArrayList<Number>();
list.add(new Integer(123));
list.add(new Double(12.34));
Number first = list.get(0);
Number second = list.get(1);

2. 编译器如果能自动推断出泛型类型,就可以省略后面的泛型类型,甚至省略后面的泛型类型后尖括号也可以省略。例如,对于下面的代码:

List<Number> list = new ArrayList<Number>();

可以省略后面的泛型类型转化为

// 这时需要编译器自己推断泛型类型
List<Number> list = new ArrayList<>();

// 甚至省略后面的泛型类型后尖括号也可以省略
List<Number> list = new ArrayList();

3. 在接口中使用泛型


4. 编写泛型类

// 定义类 Apple 时使用了泛型声明
class Apple<T> {
    // 使用T类型形参定义实例属性
    private T info;

    public Apple() {

    }

    // 下面方法中使用T类型形参来定义构造函数
    public Apple(T info) {
        this.info = info;
    }

    public void setInfo(T info) {
        this.info = info;
    }

    public T getInfo() {
        return this.info;
    }
}


public class GenericLearn {
    public static void main(String[] args) {
        // 由于传给T形参的是String,所以构造器参数只能是String
        Apple<String> a1 = new Apple<>("苹果");
        System.out.println(a1.getInfo());

        // 由于传给T形参的是Double,所以构造器参数只能是Double或double
        Apple<Double> a2 = new Apple<>(100.06);
        System.out.println(a2.getInfo());
    }
}
---------------------------------------------------------
Apple
100.06

尖括号放置说明:

20210428205844.jpg

5. 泛型类中的静态方法使用泛型

编写泛型类时,要特别注意,泛型类型<T>不能用于静态方法。无法在静态方法的方法参数和返回类型上使用泛型类型T。那我们该怎么办呢?我们可以这样对于静态方法,我们可以单独改写为“泛型”方法,只需要使用另一个类型即可。例如下面这样:

    // 编译时这将出现无法在静态上下文上引用非静态类型变量T的错误
    public static Apple<T>readObject(T a) {
        return new Apple<T>(a);
    }

单独改写为“泛型”方法后的

    public static <K> Apple<K>readObject(K a) {
        return new Apple<K>(a);
    }

具体实现:

// 定义类 Apple 时使用了泛型声明
class Apple<T> {
    // 使用T类型形参定义实例属性
    private T info;

    public Apple() {

    }

    // 下面方法中使用T类型形参来定义构造函数
    public Apple(T info) {
        this.info = info;
    }

    public void setInfo(T info) {
        this.info = info;
    }

    public T getInfo() {
        return this.info;
    }

    public static <K> Apple<K>readObject(K a) {
        return new Apple<K>(a);
    }
}


public class GenericLearn {
    public static void main(String[] args) {
        // 由于传给T形参的是String,所以构造器参数只能是String
        Apple<String> a1 = new Apple<>("Apple");
        System.out.println(a1.getInfo());

        // 由于传给T形参的是Double,所以构造器参数只能是Double或double
        Apple<Double> a2 = new Apple<>(100.06);
        System.out.println(a2.getInfo());

        Apple<Number> a3 = Apple.readObject(2021);
        System.out.println(a3.getInfo());
    }
}
----------------------------------------------
Apple
100.06
2021

6. 支持多个泛型类型

泛型还可以定义多种类型。例如,我们希望Apple不总是存储两个类型一样的对象,就可以使用类型<T, K>同时指定泛型参数类型,例如像下面这样:

class Apple<T, K> {
    private T phone;
    private K series;

    public Apple(T phone, K series) {
        this.phone = phone;
        this.series = series;
    }

    public T readPhone() {
        return phone;
    }

    public K readNumber() {
        return series;
    }
}


public class GenericLearn {
    public static void main(String[] args) {
        // 使用的时候,需要指出两种类型 [String, Integer]
        Apple<String, Integer> a1 = new Apple<>("iphone", 12);

        System.out.println(a1.readPhone());
        System.out.println(a1.readNumber());
    }
}
-----------------------------------------------------------------
iphone
12
上一篇下一篇

猜你喜欢

热点阅读