[Java] 泛型Generic

泛型Generic

目录

一、什么是泛型?

​ 泛型是JDK5中引入的,泛型是一种参数化类型。

泛型是限定参数类型的参数。它是参数,用于限定传递的对象的类型。


二、为什么要引入泛型?

​ 先来看一个例子。

​ 我们首先定义两个相似的Dog、Cat类。

class Dog{
    private String name;
    private int age;

    public Dog(String name, int age){
        this.name = name;
        this.age = age;
    }
//省略get
}

class Cat{
    private String name;
    private int age;
    public Cat(String name, int age){
        this.name = name;
        this.age = age;
    }
//省略get
}

​ 接着在测试类中进行如下操作。

public class Test{
    public static void main(String[] args){
        List list = new ArrayList();
        list.add(new Dog("小黄",5));
        list.add(new Dog("发财",2));

        //list.add(new Cat("英短",2));

        for(Object obj : list){
            Dog dog = (Dog)obj;
            System.out.println("Dog's name: "+dog.getName()+
                    ",Dog's age: "+dog.getAge());
        }
    }
}

​ 上述例子我们在动态数组中加入了两个Dog对象。在使用for-each循环遍历这个数组时,为了访问到Dog类中的私有属性,我们需要调用Dog类中的方法。对 obj 向下转型成Dog对象。

​ 到这里看是没有任何问题的,因为在向动态数组中添加元素时,我们清楚的知道添加的元素的类型,这似乎是没有什么问题的。但是,首先由于数据量小,我们可以清楚的知道元素的类型,那么数据量大的时候呢?还能保证每个被添加的元素的类型和我们想象中一致吗?第二,list这个对象变量是我们自己创建的,我们清楚的知道我要用它来存储什么类型的元素,对于其他人而言,他会认为这是一个任何类型元素都可以储存的数组,这会造成什么情况呢?

//假如现在我忘记了list用于存储Dog元素而往其中添加了一个Cat类元素
//上面注释掉的代码

list.add(new Cat("英短",2));

//而后继续调用上面的for-each循环 

[Java] 泛型Generic

抛出ClassCastException异常。

因此引入泛型就很有必要

①保证程序运行的安全。上面就是一个例子,至于如何保证安全在后面会说到。

②节约资源。这点也会在后面说到


三、泛型的使用

3.1 泛型方法

​ 由前面所言,我们知道:泛型是一种类型参数,用于限定在方法、类间传递参数的类型。

​ 我们对上面的代码进行些许的改变。

[Java] 泛型Generic

​ 改变在于在建立 对象变量list的时候多出了一个<>,Java中把这个叫做菱形运算符,在其中输入我们的类型参数。

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

​ 以该句为例,List list,<>内的Dog作为参数存在,只起限制作用它限制了传入list集合内的元素类型只能为Dog类

​ 在对list限制后,向list内添加Cat类元素,编译器会自己报错。

泛型的第一个好处,保证运行时安全。

​ 在申请对象变量时对其传入参数作出限制,在传入元素类型不一致时,编译器会自动报错,避免了元素类型强制转换时错误。

第二个好处,节约资源。

![泛型好处二 节约资源](D:\桌面\MarkDown\泛型\泛型好处二 节约资源.png)

​ 再次对上面代码进行调整,for-each中的Object obj替换成了Dog dog,在未使用泛型时,编译器为了保证减少错误,Object obj是不允许更改的。

​ 未经过泛型限定的集合输出时元素的类型转换(以Dog为例):Dog -> Object -> Dog

​ 经过泛型限定的集合输出时元素的类型转换 : Dog -> Dog-> Dog

3.2 泛型类

3.2.1 自定义泛型类

[Java] 泛型Generic

  1. 在类名后采用菱形运算符<>,并且给定参数类型的类称为自定义泛型类

  2. 参数类型通常用单个大写字母表示,理论上任何大写字母都可以,为了清晰的表示参数类型所指,通常使用E、K、V、T等

  3. 普通成员、构造函数、私有属性、数组均可以为泛型类型

  4. 泛型数组不能初始化

  5. 静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。因为静态属性、方法和类一起初始化,在类初始化时无法确定泛型属性或泛型方法的具体类型。

  6. 接上条,如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法

  7. 泛型类的类型变量在类实例化时确定


3.3 泛型接口

​ 自定义一个泛型接口Gt01。

public interface Gt01<E,V> {
    // E num; 泛型接口不能如此申明属性,因为接口属性默认为 public static final
    int num = 502;

    E print(E e);
}
public class GenericTest03 implements Gt01<String,Object>{
    									//类型变量在实现接口时确定,默认为Object

    @Override
    public String print(String s) {
        return "null";
    }

    public static void main(String[] args) {
        String s = new GenericTest03().print("?");
    }
}
  • 接口中无法定义泛型属性,因为接口属性默认为public static final
  • 类型变量在实现接口时确定,默认为Object

3.4 泛型方法

​ 1.泛型方法既可以定义在泛型类中,也可以定义在普通类中

​ 2.泛型方法也用菱形运算发标注,但与泛型类与泛型接口不同的是,标注的位置不同。

​ 3.泛型方法的类型变量只用于该方法中

public<T,U> void fly(){
    T t;
    U u;				// T,U 泛型标识字母只用于该方法中,而不能用于其存在的泛型类中
    System.out.println(t+u);
}

​ 4.使用了泛型的方法和泛型方法的区别

class Test<T>{
	public void hi(T t){
        ....
    }
    public<R,U> void fly(){
        ...
    }
}//hi()为使用了泛型的方法,fly()为泛型方法

3.5 通配符

  • 支持任意泛型类型
  • 支持A类及其A类的子类,限定泛型上限
  • 支持A类及其父类,不限于直接父亲,限定了泛型下限

四、 泛型注意事项

  • 泛型在限定时只能时引用类型,不能时基本类型

    List<Integer> list = new ArrayList<>();//正确
    List<int> list = new ArrayList<>();//错误示范
    
  • 在给出泛型的具体类型后,可以传入该类型或其子类类型

  • 泛型定义时,后面的菱形运算符内可以省略

  • [泛型类、泛型接口、泛型方法]在不给定类型参数的情况下,默认为类型参数为Object

上一篇:02_Java对象和类


下一篇:go语言 判断一个实例是否实现了某个接口interface