15.1 java泛型(初体验)
15.1.1 什么是泛型?
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
15.1.2 泛型的使用
腾讯课堂视频笔记:
1.概念:泛型就是参数化类型
-适用于对多种数据类型执行相同功能的代码。
-泛型中的类型在使用时指定。
-泛型归根到底就是“模板”。
2.泛型主要适用在集合中。
3.任意化:Object是所有类的根类,但是具体类使用的时候,需要类型强制转换的
- 多态:Object可以接受任意类型。
- 缺点:
需要类型转换;
需要类型检查;
需要处理转换错误异常。
4.泛型:使用泛型时,在实际使用之前类型就已经确定了,不需要强制转换。
5.泛型字母
- 形式类型参数(formal type parameters)即泛型字母。
- 命名:泛型字母可以随意指定(符合变量命名规则即可,包括中文),尽量使用单个的大写字母(有时候多个泛型类型时会加上数字,比如T1,T2)。
- 常见字母(见名知意)
T (Type) , K V (Key Value) , E (Element)
- 当类被使用时,会使用具体的实际类型参数(actual type argument)代替。
6.泛型方法
- 定义方法时:<字母>
- 注意: 泛型方法可以在非泛型类中
public class Test{
public static void main(String[] args){
test("a"); //T--->String
}
//定义泛型方法
public static <T> void test(T a){
System.out.println(a);
}
//定义泛型方法 extends <=
public static <T extends Closeable> void test(T...a){
for(T temp:a){
try{
if(temp != null){
temp.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
7.泛型继承、实现
public abstract class Father<T1,T2>{} //父类
(1)父类为泛型类,子类继承时:
- (不保留父类泛型) 父类擦除|指定类型时,子类按需编写
//父类擦除时,即父类泛型没有指定类型,默认为Object;这时子类可以不写泛型,也可以新增泛型
public class Child extends Father{}
public class Child<A,B> extends Father{}
//父类指定具体类型时,子类可以不写泛型,也可以新增泛型
public class Child extends Father<Integer,String>{}
public class Child<A,B> extends Father<Integer,String>{}
- (保留父类泛型)父类存在泛型,子类泛型参数个数必须 >= 父类保留泛型参数个数
//部分保留父类泛型,则子类必须要写父类保留的泛型参数,也可以新增泛型
public class Child<T1,A,B> extends Father<T1,String>{}
//全部保留父类泛型,则子类必须要写所有父类的泛型,也可以新增
public class Child<T1,T2,A> extends Father<T1,T2>{}
- 属性类型:根据所在位置而定。父类中,随父类而定;子类中,随子类而定(需要扩充笔记)
- 子类重写方法类型:随父类而定。
(2)接口为泛型接口,子类实现时,
- 与继承同理,重写方法随接口而定
8.泛型擦除
- 定义:泛型擦除是指在继承(实现)或使用时没有指定具体的类型
- 特点:一旦擦除之后按Object处理
- 依然存在警告,加上Object可以去除,但是有些画蛇添足
- 不完全等同于Object,编译不会类型检查
public static void main(String[] args){
//泛型擦除,但是存在警告
Student stu= new Student();
stu.setJavaScore("81");//以Object处理
//消除警告,使用Object
Student<Object> student= new Student<Object>();
//不完全等同于Object,编译不会类型检查
test(stu);//正确,擦除,编译通过,不会类型检查
//test(student);//错误,Object编译检查
}
public static void test(Student<Integer> a){}
9.通配符?(Wildcards)
- T、 K、 V、 E等泛型字母为有类型,类型参数赋予具体的值
- ? 未知类型,类型参数赋予不确定值,任意类型
- 只能用在声明类型、方法参数上,不能用在定义泛型类、泛型方法、泛型接口上,也不能用在创建对象。
//此处T不能够换成?
public class Student<T>{
T Score;
public static void main(String[] args){
//通配符:用在声明类型,不能这样用new Student<?>();
Student<?> stu = new Student<String>();
test(new Student<Integer>());
}
//通配符:用在声明方法参数 接收信息
public static void test(Student<?> stu){
//获取信息
System.out.print(stu.score);
}
}
10.上限extends:指定的类型必须是继承某个类,或实现某个接口,即<=(本类或子类)
- ? extends Fruit
- T extends List
- 一般用于限制操作,不能使用在添加数据上面,一般都是读取操作,即不能添加信息
- 规则:
List<Fruit> 满足 List<? extends Fruit>
List<Apple> 满足 List<? extends Fruit>
List<? extends Apple> 满足 List<? extends Fruit>
List<?> 等同 List<? extends Object> //不满足
- 思考:public void test(List<? extends Fruit> list){
list.add(new Fruit("f"));
list.add(new Pear("p"));
list.add(new Apple("a"));
} 这段代码为什么无法通过编译呢???
因为List<Fruit>、List<Apple>等都是List<? extends Fruit>的子类型。先假设传入的参数为List<Fruit>则以上代码的三个add操作都是可行的,可如果是List<Apple>,则只有第三个add可以执行,可见,为了保护其类型的一致性,也是不能往List添加任意对象的,不过却可以添加null。
11. 下限super:指定的类型不能小于操作的类,即 >= (本类或父类)
- ? super Apple
- T super Apple
- 不能添加父对象
- 规则:
List<Apple> 满足 List<? super Apple>
List<Fruit> 满足 List<? super Apple>
List<? super Fruit> 满足 List<? super Apple>
- 思考:public void test(List<? super Apple> list){
list.add(new Apple("apple"));
list.add(new FujiApple("fuji"));
list.add(new Fruit("fruit"));
} 这段代码哪一行不能通过编译,为什么?
同理,传入List<Apple>则第三行错误,不能list.add(new Fruit("fruit")),为了保护类型的一致性,因为“? super Apple”可以是Fruit,也可以是Object,因无法确定其类型,也就不能往List<? super Apple> 添加Apple的任意父类对象。
12.泛型嵌套
- 稍微复杂一些,从外到内拆分
public class Student<T>{
T score;
}
public class Bjsxt<T>{
T stu;
}
public class Main{
public static void main(String[] args){
//泛型的嵌套
Bjsxt<Student<String>> room = new Bjsxt<>();
//从外到内拆分
room.stu = new Student<String>();
Student<String> stu= room.stu;
String score = stu.score;
System.out.println(score);
}
}
13.其他
- 泛型没有多态 //A<Fruit> f = new A<Apple>();编译出错
- 没有泛型数组 //Student<String>[] stus = new Student<String>[10];编译出错
- jdk1.7简化泛型(声明指定,创建可以省略):List<String> li = new ArrayList<>();
15.1.2.1 泛型运用在class中
[访问修饰符] class 类名<泛型1,泛型2,…>{
[访问权限] 泛型类型标识 变量名称;
[访问权限] 构造方法([<泛型类型>] 参数名称){ //内部代码}
[访问权限] 返回值类型 方法名称(){ //内部代码 }
[访问权限] 返回值类型声明 方法名称(泛型类型标识 变量名称){ //内部代码}
}
例如:
/*
此处声明了一个包含泛型T的泛型类,T代表所有可能的类型,而T
的实际类型在 Test 类实例化时指定。
*/
public class Test<T> {
private T f; //f为泛型成员。在类名后面声明泛型后,在类内部可以向普通类型一样使
public Test(){
}
//用泛型
public Test(T f){
this.f = f;
}
public void setF(T f) {//setF方法的参数类型为泛型T
this.f = f;
}
public T getF() {//getF方法的返回类型为泛型T
return f;
}
}
15.1.2.2 实例化泛型类
实例化泛型类时,必须制定泛型的具体类型。例如:
test test1 = new Test();//编译时默认指定为Object
Test<String> test1 = new Test<String>();
Test<String> test1 = new Test<>();//jdk1.7泛型的简化,后面可以不写
15.1.3 泛型运用在方法中(了解)
单独在方法中运用泛型:
public <T> T test(T t) {
// 方法中的T 由 实际参数的类型决定.
return t;
}
使用:
对象引用名.<String>test("abc");//完整的写法
对象引用名.test("abc");//jdk1.7之后,可以省略泛型的指定,而是根据传入的形参的类型自动而定
15.1.4 泛型中的 通配符 ? (了解)
通配符只能用在声明泛型类的引用,和定义泛型类返回值中:
Test<?> test=new Test<Object>();//该引用可以指向 new Test<任意类型>();
public Test<?> getTest(){//可以返回 new Test<任意类型>();
return new Test<Object>();
}
15.1.5 泛型中使用 extends\super (了解)
extends 可以用在设计泛型,也能用在声明泛型类的引用,和定义泛型类返回值中
super 不能用在设计泛型,只能用在声明泛型类的引用,和定义泛型类返回值中
如:设计泛型类时
public class Test<K extends Student>{} //正确
public <T extends People> void text(T t) {//正
}
public class Test<K super Student>{} //错误
public <T super Student> void text(T t) {//错误
}
如:声明泛型类的引用,和定义泛型类返回值中
//声明泛型类的引用
Test<? extends People> test = new Test<Student>();
//定义泛型类返回值
public Test<? extends People> getTest(){
return new Test<Student>();
}
// 正确
//声明泛型类的引用
Test<? super Student> test = new Test<People>();
//定义泛型类返回值
public Test<? super Student> getTest(){
return new Test<People>();
}
// 正确