文章目录
- 前言
- 一、限制泛型类可用类型
- 二、使用类型通配符(?)
- 三、继承泛型类与实现泛型接口
- 总结
前言
本篇介绍泛型类的更深层次的用法,限制泛型定义数据类型的范围、使用类型通配符、继承泛型类与实现泛型接口。
一、限制泛型类可用类型
泛型类的类型限制是指在定义泛型类时通过使用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关键字或者通配符(?)来限制泛型的类型。