限制泛型类型、使用类型通配符和继承泛型类(接口)

文章目录


前言

        本篇介绍泛型类的更深层次的用法,限制泛型定义数据类型的范围、使用类型通配符、继承泛型类与实现泛型接口。


一、限制泛型类可用类型

        泛型类的类型限制是指在定义泛型类时通过使用extends关键字来限制可用的类型范围。泛型可以继承。

 设定泛型的上限,语法如下:

class 类名<T extends anyClass>    

        调用了 extends关键字 ,继承某类,那么只能使用这个类及其子类充当泛型。

         实操展示:使用extends关键字,将TestDemo类的泛型继承自Colloection类及其子类

import java.util.Collection;
import java.util.List;
import java.util.Set;

public class TestDemo<T extends Collection>{ 
    public static void main(String[] args) {
        TestDemo<Set> testDemo = new TestDemo<>();
        TestDemo<List> testDemo2 = new TestDemo<>();
        TestDemo<Collection> testDemo3 = new TestDemo<>();
        //在创建泛型时只能创建Collection以及其子类
    }
}

         如上代码所示,当TestDemo类继承了Colloection类,当实例化对象时,必须实现泛型该类或者其类的子类。由于map集合不是Collection的子类,如果泛型化map集合类,则会报错。

二、使用类型通配符(?)

        泛型机制中,提供了通配符,其主要作用为限制这个泛型类对象的类型实现或者继承某一接口或者类的子类。类型通配符为“?” ,同时使用 extends 或者 super 关键字加以限制。

        通配符有以下几种形式:

        1.未限定通配符(?):表示未知类型,可以匹配任意类型的实参。

        2.上限通配符(? extends 类型):表示未知类型是指定类型的子类,可以匹配指定类型或其子类的实参。

        3.下限通配符(? super 类型):表示未知类型是指定类型的父类,可以匹配指定类型或其父类的实参。

        类型通配符不能写在创建泛型类当中。

        以下是使用泛型通配符的语法如下:

泛型类名<?> 对象 = null;
//泛型类型未知

泛型类名<? extends anyClass> 对象1 = null;
//对象1的泛型只能是anyClass以及anyClass的子类

泛型类名<? super anyClass> 对象2 = null;
//对象2的泛型只能是anyClass以及anyClass的父类

        使用上面的语法时,自身类必须设定了泛型<T>。 

        实操展示:设置Demo实例化对象的泛型为继承List集合的子类

Demo<? extends List> demo = null,demo2= null;    //使用语法

demo = new Demo<ArrayList>();
ArrayList<String> arrayList = demo.getObject();
arrayList.add("Hello");
arrayList.add("World");

demo2 = new Demo<LinkedList>();
LinkedList<Integer> linkedList = demo2.getObject();
linkedList.add(1);
linkedList.add(2);

//demo 和 demo2 都泛型类实现了List集合类的子类

         由上代码所示通配符的使用,使用通配符可以使得泛型类或方法能够处理更多类型的参数,提高代码的复用性和可扩展性。另外,在涉及类型转换或泛型类型的不确定性时,使用通配符可以避免编译器的类型检查错误。但值得注意的是,一旦使用了泛型通配符,集合中的值就不能改变了。super下限通配符的用法和注意点和上面一样,区别只是泛型化了super后的类型及其父类。

三、继承泛型类与实现泛型接口

        继承泛型的四种情况:

        (1)全部继承·:

abstract class Father<T1,T2>{}
//父类

class Child<T1,T2,T3> extends Father<T1,T2>{}
//继承全部父类泛型

         Child类继承Father类时保留父类的泛型类型,需要在继承时指明。如果没有指明,直接使用 extends关键字继承操作,那么 Child类中的泛型T1,T2,T3都默认为Object类。所以1一般情况都将父类的泛型类指明保留。 

        实操展示:创建Father类和Child类,创建构造方法,分别用向上转型和子类实例化来演示了泛型类的使用和继承关系。

import java.lang.Math;
public class TestDemo{ 
    public static void main(String[] args) {
        Father<Integer,Double> father = new Child(1024, Math.PI);
        System.out.println("--------------------------------");
        Child<Integer,Double,String> child = new Child(404, "错误");
    }
}

class Father<T1,T2> {
    T1 t1;
    T2 t2;

    public Father(T1 t1,T2 t2){
        this.t1=t1;
        this.t2=t2;
        System.out.println("t1的类型:"+this.t1.getClass());
        System.out.println("t2的类型:"+this.t2.getClass());
    }
}

//Father类中此处的T3传参的是Father类原本的T2,调用构造方法时t2填写T3类型的数据
class Child<T1,T2,T3> extends Father<T1,T3>{
    public Child(T1 t1,T3 t2){
        super(t1, t2);
    }
}

        运行结果:

 

        由上图所示, 第一种向上转型,父类直接明确数据的类型,子类实例化内容后赋值给了父类对象;第二种子类实例化,子类的第一个和第三个泛型类型是构造方法的串参类型,输入类型为子类明确的T1和T3类型。

         (2)部分继承:

abstract class Father<T1,String>{}
//父类

class Child<T1,T2,T3> extends Father<T1,String>{}
//继承部分父类泛型

         之所以说是部分继承泛型,是因为子类Child只继承了父类Father的部分泛型类型T1,T2的类型已经被明确为String,因此在声明Child对象时只需要提供T1的具体类型。这样做的目的是为了灵活使用泛型,根据具体情况确定需要继承的泛型类型。

         实操展示:明确Father类的部分泛型,让其他部分的泛型被Child继承

public class lon {
    public static void main(String[] args) {
        Child<Boolean,Double,Boolean> child = new Child( true, "字符串");
        Child<Double,Double,Boolean> child2 = new Child( 3.14, "字符串");
    }
     
}

class Father<T1,T2>{
    T1 t1;
    T2 t2;
    public Father(T1 t1,T2 t2){
        this.t1=t1;
        this.t2=t2;
        System.out.println("t1的类型:"+this.t1.getClass());
        System.out.println("t2的类型:"+this.t2.getClass());
    }
}

//Child类继承了Father类的泛型T1
class Child<T1,T2,T3>extends Father<T1,String>{
    public Child(T1 t1,String t2){
        super(t1, t2);
    }
}

         运行结果:

        由上图所示,若是子类部分继承父类的泛型,意思是父类在被继承的过程中实现了一部分泛型,另一部分的泛型类型还没有确定,这部分没确定的泛型会被子类灵活使用,或为布尔值,或为浮点数等。 

        (3) 实现父类泛型:

abstract class Father<T1,T2>{}
//父类

class Child<T1,T2> extends Father<Integer,String>{}
//继承部分父类泛型

         实现父类泛型是指在子类中使用父类的泛型类型。继承父类的过程中,明确父类的泛型类型,子类进行实现父类的泛型类型,不定义其泛型类型。

        实操展示:在Child类继承Father类的过程中,明确Father类两个泛型的数据类型,使子类实现父类泛型。

public class lon {
    public static void main(String[] args) {
        Child<Boolean,Double,Boolean> child = new Child( 1, 1.12);
        System.out.println("------------------------------------");
        Child<Double,Double,Boolean> child2 = new Child( 3, 1.13);
    }
     
}

class Father<T1,T2>{
    T1 t1;
    T2 t2;
    public Father(T1 t1,T2 t2){
        this.t1=t1;
        this.t2=t2;
        System.out.println("t1的类型:"+this.t1.getClass());
        System.out.println("t2的类型:"+this.t2.getClass());
    }
}
//设置的父类泛型为 Integer和Double,子类构造方法只能是这两种类型,不能改变继承的泛型
class Child<T1,T2,T3>extends Father<Integer,Double>{
    public Child(Integer t1,Double t2){
        super(t1, t2);
    }
}

        设置的父类泛型为 Integer和Double,子类构造方法只能是这两种类型,不能改变继承的泛型 

        运行结果:

 

         (4)不实现父类泛型:

abstract class Father<T1,String>{}
//父类

class Child extends Father{}
//不实现父类泛型

        不实现父类泛型,因此所有的父类泛型类型都会变为Object类型,那么子类Child在调用父类的一切方法,参数都可以自定义类型,没有约束。 

         实操展示:

public class lon {
    public static void main(String[] args) {
        Child<Boolean,Double,Boolean> child = new Child( new Object(), 1.12);
        System.out.println("------------------------------------");
        Child<Double,Double,Boolean> child2 = new Child( 3, true);
    }
     
}

class Father<T1,T2>{
    T1 t1;  //Object t1;
    T2 t2;  //Object t2;
    public Father(T1 t1,T2 t2){
        this.t1=t1; 
        this.t2=t2;
        System.out.println("t1的类型:"+this.t1.getClass());
        System.out.println("t2的类型:"+this.t2.getClass());
    }
}

class Child<T1,T2,T3>extends Father{
    public Child(Object t1,Object t2){
        super(t1, t2);
    }
}

        运行结果:

 

        由上图所示,不实现父类泛型,父类Father的所有参数都变成了初始的Object类,子类Child可以在调用父类方法时,任意创建各种类型的数据参数。可以为Object对象,也可以使浮点数等。 


总结

        泛型的类型参数只能是类类型,不可以使简单类型,例如<long>这种泛型定义就是错误的。在本篇不少实例中不难看出,定义泛型时,可以是多个的;可以使用extends关键字或者通配符(?)来限制泛型的类型。

  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
泛型的产生条件 泛型是为了解决在编译期间无法确定类型而引入的,其产生条件包括: 1. 在代码中需要使用泛型类,但是这个泛型类的具体类型在编译期间是不确定的; 2. 在代码中需要对泛型类进行操作,比如调用其方法或者获取其属性值。 泛型的概念 泛型是指在编写代码时,不需要指定数据类型,而是在使用时再指定具体的数据类型。这样就可以提高代码的复用性和灵活性。 泛型类 泛型类是指使用泛型定义的类,其中泛型参数可以用在类的成员变量、成员方法、构造方法中。 泛型类派生类 泛型类派生类是指使用泛型定义的类的子类,其中子类可以继续使用父类中定义的泛型类。 带泛型子类 带泛型子类是指在继承泛型类时,子类也要使用泛型类。 不带泛型子类 不带泛型子类是指在继承泛型类时,子类不使用泛型类泛型接口 泛型接口是指使用泛型定义的接口,其中泛型参数可以用在接口的方法中。 泛型方法 泛型方法是指使用泛型定义的方法,其中泛型参数可以用在方法的参数列表、返回值、方法体中。 类型通配符 类型通配符是指在定义泛型使用的一种特殊符号,用于表示不确定的类型。 引出类型通配符 类型通配符可以用于引出泛型类参数的上限或者下限。 类型通配符的上限 类型通配符的上限是指使用 extends 关键字限制泛型类参数的范围,表示泛型类参数必须是某个类型的子类或者实现类。 类型通配符的下限 类型通配符的下限是指使用 super 关键字限制泛型类参数的范围,表示泛型类参数必须是某个类型的父类或者超类。 类型擦除 类型擦除是指在编译期间,将泛型类参数替换为其上限或者 Object 类型的过程。 无限制类型的擦除 无限制类型的擦除是指在泛型类参数没有明确指定上限或者下限时,将其擦除为 Object 类型。 有限制类型擦除 有限制类型擦除是指在泛型类参数有明确指定上限或者下限时,将其擦除为上限或者下限。 擦除泛型方法中类型定义的参数 在泛型方法中,如果定义了泛型类参数,则在编译期间也会进行类型擦除。 桥接方法 在泛型类或者泛型接口中,如果有泛型方法,则在编译期间会自动生成桥接方法来确保类型安全。 泛型数组 泛型数组是指使用泛型定义的数组,其中数组元素的类型泛型类参数。 泛型与反射 泛型与反射的结合可以实现动态创建泛型类对象、获取泛型类信息等功能。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喵果森森

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值