Java 核心技术JVM · Java虚拟机原理 · JVM上语言·框架· 生态系统

Java—泛型详解和使用

2020-03-20  本文已影响0人  Hughman

1 泛型介绍

1.1 泛型的出现

  泛型的出现还得从集合说起,没有泛型的时候,我们将一个对象存入集合时,集合不care这个对象的数据类型是什么,当我们再次从这个对象取出来的时候,对象的编译类型会变成Object类型,这时候我们就需要强制类型转换,但是这种行为每次都要去指定类型进行强制转换,并且有可能强制转换不了,比如我存的是Integer类型,误转换为String类型,那就可能会引发ClassCastException异常。
  当Java 5引入了一个叫做“参数化类型”的概念后,我们可以在创建集合时去指定集合,这样我们再从集合取出数据时,这个数据就是我们当初指定的类型,不会出现需要强制类型转换的情况了。这种参数化类型就是泛型

1.2 泛型在集合中的使用示例

  在集合接口或者类后面增加尖括号<>,里面注明数据类型,创建这个集合后,这个集合就只能存储这个特定的数据类型对象。
  从Java 7开始,在使用带泛型的接口、类定义变量,我们调用构造器创建对象时构造器后面不需要带完整的泛型信息,只需要带一对尖括号<>就行。如List<String> strList = new ArrayList<>();只需要前面声明<String>泛型,后面只需要<>即可。
示例:

public class DemoApplication {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        stringList.add("t1");
        stringList.add("t11");
        Map<String, List<String>> map = new HashMap<>();
        map.put("t", stringList);
        stringList.forEach(str -> System.out.println(str.length()));
        map.forEach((key, value) -> System.out.println(key + "---->" + value));
    }
}

结果:

2
3
t---->[t1, t11]

2 泛型的进阶

  泛型就是允许在定义接口、类和方法时使用类型形参,通过传入实际的类型参数(类型实参),该类型形参会在声明变量、创建对象、调用方法的时候动态指定。类型形参可以在整个接口、类内当作类型使用。这种方式可以动态生成无限个逻辑上的子类(实际物理中没有)。
  无论泛型的类型形参传入的是什么类型实参,系统最后都是当作一个类来处理,内存中也只是占用一块内存空间。如List<Integer> intList = new ArrayList<>();List<String> strList = new ArrayList<>();通过intList.getClass()strList.getClass()得到的是相等的结果,这就表明两个类是相同的。

另: 在静态变量和静态方法声明中不可以使用类型形参。

2.1 泛型接口举例

public interface List<T> {
    void create(T e);
    void delete(T e);
    void update(T e);
    void find(T e);
}

其中,List为例,若形参T传入的是String类型的实参,则会产生一个List<String>,逻辑上是List的子接口,只不过这个接口内的E类型都为String类型。

2.2 泛型类举例

定义泛型类Person

//创建带泛型声明的自定义类
public class Person<T>{
    
    //使用T类型形参来定义实例变量
    private T info;
    
    public Person(){}
    
    //使用T类型形参定义构造器,注意:构造器不用增加泛型声明,直接使用原类名即可;
    public Person(T info) {
        this.info = info;
    }

    public T getInfo() {
        return info;
    }

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

泛型形参传入实参测试:

public class DemoApplication {
    public static void main(String[] args) {
        
        //实参传入String类型给T形参,则构造器构造对象时传参为String类型;
        Person<String> personStr = new Person<>("小明");
        System.out.println("姓名: " + personStr.getInfo());
        
        //实参传入Integer类型给T形参,则构造器构造对象时传参为Integer类型;
        Person<Integer> personInt = new Person<>(28);
        System.out.println("年龄: " + personInt.getInfo());
    }
}

结果:

姓名: 小明
年龄: 28

从上面的实验中,我们可以看出:通过泛型类Person<T>传入实参后,可以生成诸如Person<String>Person<Integer>Person<Float>等多个逻辑子类。

2.3 泛型类无参子类

创建ChinesePerson类继承无参泛型类。

public class ChinesePerson extends Person {
    @Override
    public String getInfo() {
        return super.getInfo().toString();
    }
}

测试:

public class DemoApplication {
    public static void main(String[] args) {
    
        Person<String> chinesePerson = new ChinesePerson();
        chinesePerson.setInfo("中国人");

        System.out.println("Chinese person: " + chinesePerson.getInfo());
    }
}

结果:

Chinese person: 中国人

2.5 泛型类带参子类

创建JiangsuPerson类继承带参泛型类。

public class JiangsuPerson extends Person<String> {
    //重写父类方法,返回值类型需保持一致
    public String getInfo() {
        return "城市信息: " + super.getInfo();
    }
}

测试:

public class DemoApplication {
    public static void main(String[] args) {

        Person<String> jiangsuPerson = new JiangsuPerson();
        jiangsuPerson.setInfo("南京");
        System.out.println(jiangsuPerson.getInfo());

    }
}

结果:

城市信息: 南京

3 泛型的类型通配符

3.1 类型通配符<?>

  通配符?在类型形参中使用后,可以表示各种泛型的父类,如List<?>,即List的类型未知,可以传入任何类型的实参。
  这种List<?>不能直接添加元素,因为无法确定集合内的类型是什么,所以无法添加对象。(除了null,因为null是所有引用类型的实例)
  List<?>通过get()方法返回的一定是Object,所以可以将其赋值给Object类型变量。

3.2 类型通配符上限<? extends xx>

  通过<? extends xx>表示所有xx泛型的父类,如List<? extends Person>表示所有Person泛型List的父类。?代表的是一个未知类型,但这个未知类型又受到extends限制,之鞥呢是Person的自身及子类。

3.3 类型形参的上限

  类型形参的上限主要是限制实参只能是形参类型本身或子类。
示例:

public class Person<T extends Number> {
    T info;
    public static void main(String[] args) {
        Person<Integer> person = new Person<>();
        Person<Float> person2 = new Person<>();
    }
}

若上述代码中增加Person<String> person3 = new Person<>();,则编译报错,因为String类型不是Number或子类;通过<T extends xx>限制住形参的上限。

上一篇下一篇

猜你喜欢

热点阅读