泛型
hashMap例子
public static void test(){
HashMap<String, Integer> map = new HashMap<>();
map.put("name",14);
map.put("pwd",123);
map.put("age",24);
Set<Map.Entry<String, Integer>> entries = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
while (iterator.hasNext()){
Map.Entry<String, Integer> next = iterator.next();
System.out.println("key:"+next.getKey()+",value:"+next.getValue());
}
}
/** result:
key:name,value:14
key:pwd,value:123
key:age,value:24
*/
泛型类、方法
自定义泛型类
例如:
class Person4<T> {
private String name;
private T t;
public Person4(){
T[] arr = new T[10] //编译不通过
T[] arrT = (T[]) new Object[10]; //可以这样做
}
public Person4(String name, T t){
this.name = name;
this.t = t;
}
public T getT(){
return t;
}
public void setT(T t){
this.t = t;
}
@Override
public String toString() {
return "Person4{" +
"name='" + name + '\'' +
", t=" + t +
'}';
}
//静态方法中不能使用类的泛型
public static void show(T t){ //编译错误
System.out.println(t);
}
}
//异常类不能是泛型的
class MyException<T> extends Exception{ } //编译错误
try{
}catch(T t){ //编译错误
}
总结
- 集合接口或集合类在jdk5.0时都修改为带泛型的结构。
- 在实例化集合类时,可以指明具体的泛型类型,指明完以后,
在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)
使用到类的泛型的位置,都指定为实例化的泛型类型。比如:add(E e)--->实例化以后:add(Integer e)
- 泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,用包装类替换
- 如果实例化时,没有指明泛型的类型。默认类型为为
java.Lang.Object
类型 - 静态方法中不能使用类的泛型。
- 异常类不能是泛型的
泛型方法
- 在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
- 换句话说,泛型方法所属的类是不是泛型类都没有关系。
- 泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
/**
格式:类型变量(这里是T)放在修饰符(这里是 public static) 的后面,返回类型(这里是test01)的前面。
该方法里面有泛型结构:<T>所以它是泛型方法
*/
class Person<T>{
public static <E> E test01(E e){ //T 和 E 不相干
return e;
}
}
/**
可以通过对类型变量T设置限定(bound)
T 可以实现多个接口 并且只能继承一个类,这里都用extends表示,
如果T继承了类,则这个类一定是限定列表的第一个。
public static <T extends Comparable> T test01(T t)
也可用多个限定,此时用 & 分隔开
public static <T extends Comparable & Serializable> T test01(T t)
*/
泛型类型的继承原则
- 假设
employee
是manager
的超类,则ABC<Manager>
不是ABC<Employee>
的子类。 - 即无论 E 与 T 有什么联系,
ABC<E>
与ABC<T>
没有什么联系.
如下:
ABC<Manager> managerBuddies = ...;
ABC<Employee> employeeBuddies = managerBuddies; // 编译时就会出错
类型擦除(方法擦除,变量擦除)
1. Java 泛型转换的事实:
- 虚拟机中没有泛型, 只有普通的类和方法。
- 所有的类型参数都用它们的限定类型替换。
- 桥方法被合成来保持多态。
- 为保持类型安全性,必要时插人强制类型转换。
2. 例子 - 泛型类型都有一个原始类型与之对应,原始类型就是删除类型参数后的泛型类型名。
- 其中类型变量,替换为限定类型,若有多个类型,则替换为第一个限定类型。
无限定类型时 用Object代替。如
public class Generic<T>{
T name;//变量
public static <T> T test01(T t){ //方法
}
}
//对应的原始类型为
public class Generic{
Object name;
public static Object test01(T t){
}
}
//当有多个限定时
public class ABC<T extends Comparable & Serializable>{
T name;//变量
public static <T> T test01(T t){ //方法
}
}
//对应的原始类型为
public class ABC{
Comparable name;//变量
public static Comparable test01(T t){ //方法
}
}
Java 泛型 约定和局限
- 不能用基本类型实例化类型参数,如不能使用
ABC<double>
,可以用ABC<Double>
- 运行时类型查询只适用于原始类型。如
if (a instanceof Pair<String>) // Error
if (a instanceof Pair<T>) // Error
Pair<String> p = (Pair<String>) a; // Warning-can only test that a is a Pair
Pair<String> stringPair = . .
Pair<Employee〉employeePair = . .
if (stringPair.getClass() == employeePair.getClass()) // they are equal
//其比较的结果是 true, 这是因为两次调用 getClass 都将返回 Pair.class。
通配符类型
通配符:?
例子1:
List<Object> list1 = null;
List<String> list2 = null;
list1 = list2; //编译不通过
List<?> list = ...
list = list1;
list = list2; //可以这样做
例子2:
List<String> stringList = new ArrayList<>();
stringList.add("AA");
stringList.add("BB");
stringList.add("CC");
List<?> list = stringList;
//对于List<?> 不能向里面添加数据,但是可以添加null
//list.add("dd");//编译报错
Object o = list.get(0);
System.out.println(o); //result: AA
有限制条件的通配符
ABC<? extends Employee>
其中问号可以是一切类型,但需要是employee
的子类。此时泛型之间的继承可以有如下关系:
ABC<Manager>
是ABC<? extends Employee>
的子类型- 上届:
<? extend ABC>
?处要匹配的类型需要是ABC的子类或者ABC(即?需要小于等于ABC) - 下届:
<? super ABC>
?处要匹配的类型需要是ABC的超类或者ABC(即?需要大于等于ABC)可以为方法提供参数, 但不能使用返回值。 - 直观地讲,带有超类型限定的通配符可以向泛型对象写人,带有子类型限定的通配符可以从泛型对象读取。
例如
public static void test(){
List<? extends Person4> list1 = null;
List<? super Person4> list2 = null;
List<subPerson4> list3 = null;
List<Person4> list4 = null;
List<Object> list5 = null;
list1 = list3;
list1 = list4;
//list1 = list5; //编译不通过
//list2 = list3; //编译不通过
list2 = list4;
list2 = list5;
list1 = list3;
list2 = list4;
//读数据
Person4 person4 = list1.get(0);
Object o = list2.get(0);
//写数据
//list1.add(new subPerson4()); //编译不通过
list2.add(new Person4());
list2.add(new subPerson4()); //多态形式
//Person4的父类 编译不通过
//list2.add(new Object()); //编译不通过
}