泛型概述
- Java SE7及以后的版本中,构造方法中可以省略泛型类型,例如List<String> list = new ArrayList<>();
- 泛型变量声明的类型必须匹配传递给实际对象的类型,例如下面这种是不可以的,Cat是Animal类的子类,但是List<Animal> list = new ArrayList<Cat>();这种是不被允许的,会报错,Error:不兼容的类型: java.util.ArrayList<com.imooc.Cat>无法转换为java.util.List<com.imooc.Animal>,只可以写成类似这种List<Cat> list = new ArrayList<Cat>();。
- 泛型作为参数传递给其的对象必须是同一类型,不能是其子类
- 若需要兼容其子类,需要在前面添加T extends,此处T是啥不重要
- 若需要兼容其父类,需要在前面添加T super,此处T是啥不重要
作用
- 提高java程序的类型安全:在集合中可以添加Object类型的对象,如果在不使用泛型的情况下定义了一个ArrayList对象,那么各种类的对象都可以添加到该集合中,而在从集合中取值时,都需要进行强制类型转换,可以把取出的对象转换成任意类型,但是编译时不会报错,但是运行时会发生异常
- 消除强制类型转换:泛型可以消除源代码中的许多强制类型转换,这样可以使代码的可读性更好,并减少出错的机会
泛型作为方法参数和方法重载
先上代码
Goods是Book的父类
abstract public class Goods {
public abstract void sell();
}
public class Book extends Goods{
@Override
public void sell() {
System.out.println("sell books");
}
}
下面我们来看看
- 泛型作为参数传递给其的对象必须是同一类型,不能是其子类
- 若需要兼容其子类,需要在前面添加? extends
public class GoodsSeller {
// 泛型作为方法参数,在前面添加? extends,则可以传入该类即其子类的列表
// 即列表中的元素可以是Goods类型的,也可以是其子类,例如Book等
public void sellGoods(List<? extends Goods> goods){
// 调用sell方法
System.out.println("sellGoods: ");
for(Goods good: goods)
good.sell();
}
// 泛型作为参数,传进来的必须和声明的是一致的,即列表中的对象只能是Goods类型的,不能使其子类
public void sellMethod(List<Goods> goods){
System.out.println("sellMethod: ");
for(Goods good: goods)
good.sell();
}
}
public class GoodsTest {
public static void main(String[] args){
//定义book相关的List
List<Book> books = new ArrayList<Book>();
books.add(new Book());
books.add(new Book());
GoodsSeller goodsSeller = new GoodsSeller();
// 报错,不兼容的类型: java.util.List<com.imooc.generic.Book>
// 无法转换为java.util.List<com.imooc.generic.Goods>
// goodsSeller.sellMethod(books);
goodsSeller.sellGoods(books); // 这样就可以了
}
}
关于方法重载
- 泛型作为参数的方法总是最后一个调用的
- 当我们用T(T extends xxx)做类型时,不能再以xxx这个类型作为重载,例子如下:
public class GenericMethod {
public <T extends Number> void printValue(T t){
System.out.println("泛型:" + t);
}
// 和第一个T作为参数的方法形成重载,泛型作为参数的方法总是最后一个调用的
public void printValue(Integer t){
System.out.println("Integer:" + t);
}
// 报错:名称冲突: printValue(java.lang.Number)和<T>printValue(T)具有相同疑符
// 即和第一个T作为参数的方法一样,说明他们虽然表面看来不一样,但编译后的形式是一样的,故不合法
// public void printValue(Number t){
// System.out.println("Integer:" + t);
// }
public static void main(String[] args){
GenericMethod gm = new GenericMethod();
gm.printValue(123);
gm.printValue(5.0f);
gm.printValue(10.0);
}
}
自定义泛型类
就直接上代码吧,好像没啥要说的
// 相当于把类进行了参数化
public class NumberGeneric<T> {
private T num;
public T getNum(){
return num;
}
public void setNum(T num) {
this.num = num;
}
public static void main(String[] args){
NumberGeneric<Integer> intNumber = new NumberGeneric<>();
intNumber.setNum(3);
System.out.println("Integer: " + intNumber.getNum());
NumberGeneric<Double> doubleNumber = new NumberGeneric<>();
doubleNumber.setNum(6.);
System.out.println("Double: " + intNumber.getNum());
}
}
输出
Integer: 3
Double: 3
public class TwoNumGeneric<T, X> {
private T num1;
private X num2;
public void genNum(T num1, X num2){
this.num1 = num1;
this.num2 = num2;
}
public T getNum1() {
return num1;
}
public void setNum1(T num1) {
this.num1 = num1;
}
public X getNum2() {
return num2;
}
public void setNum2(X num2) {
this.num2 = num2;
}
public static void main(String[] args){
TwoNumGeneric<Integer, Float> numObj = new TwoNumGeneric<>();
numObj.genNum(25, 5.f);
System.out.println("num1 = " + numObj.getNum1());
System.out.println("num2 = " + numObj.getNum2());
}
}
输出:
num1 = 25
num2 = 5.0
参考:慕课网-Java体系课