小白学JAVA之十八——泛型

小白学JAVA之十八——泛型

一. 基本概念

  • 通常情况下集合中可以存放不同类型的对象,是因为将所有对象都看做Object类型放入的,因此从集合中取出元素时也是Object类型,为了表达该元素真实的数据类型,则需要强制类型转换,而强制类型转换可能会引发类型转换异常。
  • 为了避免上述错误的发生,从Java5开始增加泛型机制,也就是在集合名称的右侧使用<数据类型>的方式来明确要求该集合中可以存放的元素类型,若放入其它类型的元素则编译报错。
  • 泛型只在编译时期有效,在运行时期不区分是什么类型。

二. 底层原理

泛型的本质就是参数化类型,也就是让数据类型作为参数传递,其中E相当于形式参数负责占位,而使用集合时<>中的数据类型相当于实际参数,用于给形式参数E进行初始化,从而使得集合中所
有的E被实际参数替换,由于实际参数可以传递各种各样广泛的数据类型,因此得名为泛型。

  • 如:
  • //其中i叫做形式参数,负责占位
    //int i = 10;
    //int i = 20;
    public static void show(int i) {

    }
    //其中10叫做实际参数,负责给形式参数初始化
    show(10);
    show(20);
  • //其中E叫做形式参数,负责占位
    //E = String;
    //E = Integer;
    public interface List {

    }
    //其中String叫做实际参数
    List lt1 = …;
    List lt2 = …;

三.自定义泛型接口

泛型接口和普通接口的区别就是后面添加了类型参数列表,可以有多个类型参数,如:<E, T, … >
等。

四. 自定义泛型类

  • 泛型类和普通类的区别就是类名后面添加了类型参数列表,可以有多个类型参数,如:<E, T, … >等。
  • 实例化泛型类时应该指定具体的数据类型,并且是引用数据类型而不是基本数据类型。
  • 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型。
  • 子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型。

五. 自定义泛型方法

  • 泛型方法就是我们输入参数的时候,输入的是泛型参数,而不是具体的参数。我们在调用这个泛型方法的时需要对泛型参数进行实例化。
  • 泛型方法的格式:
    [访问权限] <泛型> 返回值类型 方法名([泛型标识 参数名称]) { 方法体; }
  • 在静态方法中使用泛型参数的时候,需要我们把静态方法定义为泛型方法。

*例如:

public class SubPerson extends Person  
// 不保留泛型并且没有指定类型,此时Person类中的T默认为Object类型  
public class SubPerson extends Person<String> 
 // 不保留泛型但指定了泛型的类型,此时Person类中的T被指定为String类型
public class SubPerson<T> extends Person<T>  
// 保留父类的泛型  可以在构造对象时来指定T的类型
public class SubPerson<T, T1> extends Person<T> { 
// 保留父类的泛型,同时在子类中增加新的泛型
}*

其中,Person类代码如下:

public class Person<T> {
    private String name;
    private int age;
    private T gender;

    public Person() {
    }

    public Person(String name, int age, T gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    // 不是泛型方法,该方法不能使用static关键字修饰,因为该方法中的T需要在new对象时才能明确类型
    public /*static*/ T getGender() {
        return gender;
    }

    public void setGender(T gender) {
        this.gender = gender;
    }

    // 自定义方法实现将参数指定数组中的所有元素打印出来
    public static <T1> void printArray(T1[] arr) {
        for (T1 tt: arr) {
            System.out.println("tt = " + tt);
        }
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                '}';
    }
}

SubPerson测试类:

public class SubPersonTest {

    public static void main(String[] args) {

        // 1.声明SubPerson类型的引用指向SubPerson类型的对象并调用set方法进行测试
        SubPerson<String> sp1 = new SubPerson();  //如果SubPerson类中支持泛型
        SubPerson sp1 = new SubPerson();  //如果SubPerson类中不支持泛型
        sp1.setGender("女");

        System.out.println("----------------------------------------");
       SubPerson<Boolean> sp2 = new SubPerson<>();//如果SubPerson类中支持一个泛型
        SubPerson<Boolean, String> sp2 = new SubPerson<>();//如果SubPerson类中支持两个泛型
        sp2.setGender(true);
    }
}

Person测试类:

public class PersonTest {

    public static void main(String[] args) {

        // 1.声明Person类型的引用指向Person类型的对象
        Person p1 = new Person("zhangfei", 30, "男");
        // 2.打印对象的特征
        System.out.println(p1); // zhangfei 30 男

        System.out.println("-----------------------------------");
        // 3.在创建对象的同时指定数据类型,用于给T进行初始化
        Person<String> p2 = new Person<>();
        p2.setGender("女");
        System.out.println(p2); // null  0  女

        System.out.println("-----------------------------------");
        // 4.使用Boolean类型作为性别的类型
        Person<Boolean> p3 = new Person<>();
        p3.setGender(true);
        System.out.println(p3); // null 0  true

        System.out.println("-----------------------------------");
        // 5.调用泛型方法进行测试
        Integer[] arr = {11, 22, 33, 44, 55};
        Person.printArray(arr); // 11 22 33 44 55
    }
}

六. 泛型在继承上的体现

如果B是A的一个子类或子接口,而G是具有泛型声明的类或接口,则G并不是G的子类型!

  • 比如:String是Object的子类,但是List并不是List的子类。

七. 通配符

  • 有时候我们希望传入的类型在一个指定的范围内,此时就可以使用泛型通配符了。
    如:之前传入的类型要求为Integer类型,但是后来业务需要Integer的父类Number类也可以传入。
  • 泛型中有三种通配符形式:
    • <?> 无限制通配符:表示我们可以传入任意类型的参数。
    • <? extends E> 表示类型的上界是E,只能是E或者是E的子类。
    • <? super E> 表示类型的下界是E,只能是E或者是E的父类。
public class GenericTest {

    public static void main(String[] args) {

        // 1.声明两个List类型的集合进行测试
        List<Animal> lt1 = new LinkedList<>();
        List<Dog> lt2 = new LinkedList<>();
        // 试图将lt2的数值赋值给lt1,也就是发生List<Dog>类型向List<Animal>类型的转换
        lt1 = lt2; // Error: 类型之间不具备父子类关系

        System.out.println("---------------------------------------------");
        // 2.使用通配符作为泛型类型的公共父类
        List<?> lt3 = new LinkedList<>();
        lt3 = lt1; // 可以发生List<Animal>类型到List<?>类型的转换
        lt3 = lt2; // 可以发生List<Dog>类型到List<?>类型的转换

        // 向公共父类中添加元素和获取元素
        lt3.add(new Animal()); //Error: 不能存放Animal类型的对象,可能?类型是animal子类
        lt3.add(new Dog());   // Error: 不能存放Dog类型的对象,,可能?类型是dog子类 
        //因此?通配符不支持元素的添加操作

        Object o = lt3.get(0);  // ok,支持元素的获取操作,全部当做Object类型来处理

        System.out.println("---------------------------------------------");
        // 3.使用有限制的通配符进行使用
        List<? extends Animal> lt4 = new LinkedList<>();
        // 不支持元素的添加操作,同理,? extends Animal类型可能比以下类型都低
        //lt4.add(new Animal());
        //lt4.add(new Dog());
        //lt4.add(new Object()); 均错误
        // 获取元素
        Animal animal = lt4.get(0);

        System.out.println("---------------------------------------------");
        List<? super Animal> lt5 = new LinkedList<>();
        lt5.add(new Animal());
        lt5.add(new Dog());
        lt5.add(new Object());  //Error: 超过了Animal类型的范围,? super Animal类型可能比Object类低
        Object object = lt5.get(0);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值