泛型的由来
因为JAVA中假如构建了一个object集合,在集合里存储任何的数据类型对象,定义了一个字符串,又定义一个常数。呢么在遍历数组Arraylist的时候,在代码行里并不会报错,但是运行之后会出现ClassCastException异常。
JAVA泛型提供了编译时类型安全检测机制,在代码行里就可以看到报错啦,能够检测到非法的类型数据结构,而不是在运行才能看到。
泛型的本质就是参数化类型,也就是所操作的数据类型是指定为一个参数。
泛型类
得到代码的复用
class 类名称 <泛型标识,泛型标识,...> {
private 泛型标识 变量名;
......
}
常用的泛型标识:T,E,K,V,N
T - Type(Java 类)
E - Element (在集合中使用,因为集合中存放的是元素)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的 java 类型
使用语法
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
Java1.7之后,后面的<>中的具体的数据类型可以省略不写
类名<具体的数据类型> 对象名 = new 类名<>();
泛型类的定义
package demo1;
/**
* 泛型类的定义
* @param <T> 泛型标识-类型形参
* T 创建对象的时候里指定具体的数据类型
*/
public class Generic<T> {
// T是由外部使用类的时候来指定的
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
public String toString() {
return "Generic{" +
"key=" + key +
"}";
}
}
泛型类
package demo1;
public class MainClass {
public static void main(String[] args) {
//泛型类在创建对象的时候,来指定操作的具体数据类型。
Generic<String> strGeneric = new Generic<>("abc");
String key1 = strGeneric.getKey();
System.out.println("key1:" + key1);
输出结果:key1:abc
试一下定义integer型
Generic<Integer> integerGeneric = new Generic<>(100);
int key2 = integerGeneric.getKey();
System.out.println("key2:" + key2);
运行得到
key1:abc
key2:100
泛型类注意:
泛型类,如果没有指定具体的数据类型,此时,操作类型是object
泛型的类型参数只能是类类型,不能是基本数据类型(不能用int,用interger)
泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同的类型。
第三条难理解的话 上代码
System.out.println(intGeneric.getClass());
System.out.println(strGeneric.getClass());
输出结果:
class demo1.Generic
class demo1.Generic
是一样的
从泛型类派生子类
子类也是泛型类,子类和父类的泛型类型要一致。
class ChildGeneric<T> extends Generic<T>
子类不是泛型类,父类要明确泛型的数据类型。
class ChildGeneric extends Generic<String>
第一种情况
泛型类派生子类,子类也是泛型类,呢么子类的泛型式表示要和父类一致
父类
package demo2;
public class Parent<E> {
private E value;
public E getValue() {
return value;
}
public void setValue(E value) {
this.value = value;
}
}
子类
package demo2;
public class ChildFilrst<T> extends Parent<T> {
public T getValue(){
return super.getValue();
}
}
主类
package demo2;
public class TEST {
public static void main(String[] args) {
ChildFilrst<String> childFilrst = new ChildFilrst<>();
childFilrst.setValue("abc");
String value = childFilrst.getValue();
System.out.println(value);
}
}
运行得到 abc
第二种情况
泛型类派生子类,如果子类不是泛型类,呢么父类要明确数据类型
ChildSecond childSecond = new ChildSecond();
childSecond.setValue(100);
Integer value1 =childSecond.getValue();
System.out.println(value1);
泛型接口
定义语法
interface 接口名称 <泛型标识,泛型标识,...> {
泛型标识 方法名();
......
}
泛型接口的使用
实现类不是泛型类,接口要明确数据类型
实现类也是泛型类,实现类和接口的泛型类型要一致。
第一种
实现泛型接口的类,不是泛型类,需要明确实现泛型接口的数据类型
这里Test类不是泛型类,所以要明确泛型接口的数据类型 这里定义T是String类型
public class Test extends Generator<String> {
实现类
package demo3;
public class Test extends Generator<String> {
public String getKey() {
return "hello generic";
}
}
泛型接口类
package demo3;
/**
* 泛型接口
*/
public class Generator<T> {
T getKey() {
return null;
}
}
主类
package demo3;
public class main {
public static void main(String[] args) {
Test test = new Test();
String key = test.getKey();
System.out.println(key);
}
}
运行得到
hello generic
泛型接口的实现类,是一个泛型类,呢么要保证实现接口的泛型类的泛型标识包含泛型接口的泛型标识
实现类Test1
package demo3;
public class Test1<T> extends Generator<T> {
private T key;
public Test1(T key) {
this.key = key;
}
public T getKey() {
return key;
}
}
主类
Test1<String> test1 = new Test1<>("generic");
String key1 = test1.getKey();
System.out.println(key1);
}
}
输出 generic
泛型方法
泛型类:是在实例化类的时候指明泛型的具体类型
泛型方法:是在调用方法的时候指明泛型的具体类型
语法
修饰符 <T,E, ...> 返回值类型 方法名(形参列表) {
方法体...
}
public 与返回值中间<T> 非常重要,可以理解为声明此方法为泛型方法。
只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法
<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T,E,K,V等形式的参数常用于表示泛型,
Test.java
package demo4;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ProductGetter<Integer> productGetter = new ProductGetter<>();
ArrayList<String> strList = new ArrayList<>();
strList.add("电脑");
strList.add("手机");
strList.add("机器人");
//泛型方法的调用,类型是通过调用方法的时候来指定。
String product1 = productGetter.getProduct(strList);
System.out.println(product1 + "\t" + product1.getClass().getSimpleName());
}
}
package demo4;
import java.util.ArrayList;
import java.util.Random;
public class ProductGetter<T> {
static Random random = new Random();
//奖品
private T product;
//奖品池
ArrayList<T> list = new ArrayList<>();
/**
* 添加奖品
*/
public void addProduct(T t) {list.add(t);}
/**
* 抽奖
*/
public T getProduct() {
product = list.get(random.nextInt(list.size()));
return product;
}
/**
* 定义泛型方法
* list 参数
* <E>泛型标识,具体类型由调用方法的时候来指定
*/
public <E> E getProduct (ArrayList<E> list) {
return list.get(random.nextInt(list.size()));
}
}
运行得到:
电脑 String
java类里面的泛型和方法里面的泛型是完全无关的,
java类里的泛型假如是T ,而方法里的也是T
public <T> T getProduct (ArrayList<T> list) {
return list.get(random.nextInt(list.size()));
在这里定义了T已经是integer类型
ProductGetter<Integer> productGetter = new ProductGetter<>();
但是运行没有变化
即使定义泛型标识的名字和泛型类里面的标识是相同的,但是在使用上,我们泛型方法里面的泛型和泛型类里的泛型是没有任何关系的,是独立存在的。
泛型方法与可变参数
泛型可变参数的定义
public <E> void print(E... e) {
for (E el : e) {
System.out.println(e);
}
}
/**
* 泛型可变参数的定义
*/
public static <E> void print(E... e) {
for(int i = 0; i < e.length; i++ ) {
System.out.println(e[i]);
}
}
productGetter.print(1,2,3,4,5);
productGetter.print("a","b","c");
泛型方法总结
泛型方法能使方法独立于类而产生变化
如果static方法要使用泛型能力,就必须使其成为泛型方法。
类型通配符
类型通配符一般是使用"?"代替具体的类型实参。
所以,类型通配符是类型实参,而不是类型形参。
Test.java
package demo5;
public class Test {
public static void main(String[] args) {
Box<Number> box1 = new Box<>();
box1.setFirst(100);
showBox(box1);
Box<Integer> box2 = new Box<>();
box2.setFirst(200);
showBox(box2);
}
public static void showBox(Box<Number> box) {
Number first = box.getFirst();
System.out.println(first);
}
}
Box,java
package demo5;
public class Box<E> {
private E first;
public E getFirst() {
return first;
}
public void setFirst (E first) {
this.first = first;
}
}
我们这里
public static void showBox(Box<Number> box) {
定义的是number类型,在第二条
Box<Integer> box2 = new Box<>();
咱们用的是Integer类型
所以出现了报错 呢么怎么办呢?
引用了类型通配符
public static void showBox(Box<?> box) {
类型通配符的上限
语法
类/接口<? extends 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的子类类型。
public static void showBox(Box<? extends Number> box) {
Number first = box.getFirst();
System.out.println(first);
决定了最大上限只能是Number
类型通配符的下限
类/接口<? super 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的父类类型。
public static void showBox(Box<? super Number> box) {
Object first = box.getFirst();
System.out.println(first);
这里定义类型只能是Object,不能是Number。不然会报错(因为super定义了下限。)
Super叫做下限,因为要从指定的参数往上看
extends叫做上限,因为要从参数往下看
集合比较器用super,因为确定了所有类型不可能超过已经规定的类型,保证安全地比较。