泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型。也就是说在创建对象或调用方法时才知道具体的类型,而在定义类或方法时不需要明确,而是使用通用的类型代替。在使用时把类型当做参数进行传递。
设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常。
泛型的由来:早期Java是使用Object来代表任意类型的,但是向下转型有强转的问题,这样程序就不太安全。而使用泛型,不用强制转换使得代码更加简洁,提升了可读性和稳定性。
泛型有几种的常用的值,如下表
值 | 说明 |
T | Type的简称,代表java类 |
E | Element的简称,代表元素 (用在集合中) |
K | Key的简称,代表键,主要用于Map类 |
V | Value的简称,代表值,主要用于Map类 |
? | 类型通配符,表示不确定的类型,可使用任意类型 |
把泛型定义在类上,使用该类的时候,才把类型明确下来。然后在方法中就可以使用传入的类型。
1)定义泛型类
需求:传入一个List集合,无论什么类型,只要有元素就取第一条数据。
import java.util.List; public class MyClass<T> { // 1 public T getFirst(List<T> list) { //2 if (list == null || list.size() == 0) { return null; } return list.get(0); } }
在代码中,1处的<T>是给类添加了泛型。2处有两个T,前面的T是指定方法的返回值。后面的<T>是给List类添加了泛型,从而保证List的元素类型和方法返回值一致。
2)使用泛型类
public static void main(String[] args) { MyClass<String> myClass = new MyClass<>(); List<String> list = new ArrayList<>(); list.add("张三"); list.add("tom"); System.out.println(myClass.getFirst(list));//张三 MyClass<Map<String, String>> myClass2 = new MyClass<>(); List<Map<String, String>> list2 = new ArrayList<>(); Map<String, String> map = new HashMap<>(); map.put("name", "tom"); Map<String, String> map2 = new HashMap<>(); map2.put("age", "20"); list2.add(map); list2.add(map2); System.out.println(myClass2.getFirst(list2));//{name=tom} }
当传入String类型时,就返回String类型;传入Map类型时就返回Map类型。
若仅仅在某一个方法上需要使用泛型,那么就不需要对整个类添加泛型,而是给方法添加泛型即可。
将上述的泛型类进行修改,变成泛型方法:
1)定义泛型方法
import java.util.List; public class MyClass { public <T> T getFirst(List<T> list) { if (list == null || list.size() == 0) { return null; } return list.get(0); } }
在方法上有三个T,第一个<T>是指定此方法是一个泛型方法,第二个和第三个T的用法同上。
2)使用泛型方法
public static void main(String[] args) { MyClass myClass = new MyClass(); List<String> list = new ArrayList<>(); list.add("张三"); list.add("tom"); System.out.println(myClass.getFirst(list));//张三 MyClass myClass2 = new MyClass(); List<Map<String, String>> list2 = new ArrayList<>(); Map<String, String> map = new HashMap<>(); map.put("name", "tom"); Map<String, String> map2 = new HashMap<>(); map2.put("age", "20"); list2.add(map); list2.add(map2); System.out.println(myClass2.getFirst(list2));//{name=tom} }
通过对比可以发现,这种方式比泛型类简单一点,但效果是一样的,可根据需求进行选择。
由于E表示元素,主要用在List集合,因此直接放List的部分源码:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8683452581122892189L; .... }
这是ArrayList类的定义,使用E作为泛型。
由于K、V表示键和值,主要用在Map中,因此直接放Map的部分源码:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { private static final long serialVersionUID = 362498820763181265L; ... }
?虽然可以表示任何类型,但在单独使用时只能用在方法的参数类型上。
需求:遍历不同类型的集合的元素并打印
public void listFor(List<?> list){ if (list != null && list.size() != 0) { list.forEach(item->{ System.out.println(item); }); } }
这里使用?代替了集合的元素类型。
上一小节,传递的参数都是先创建了对象并赋值,再把这个对象通过参数进行传递的。那么当只需要明确参数的类型,不需要对象中的值时,可以使用Class<?>。可以用在类的类型和方法的参数上。
1)用在方法的参数上
public void getClassField(Class<?> pojoClass) throws NoSuchFieldException { Field field = pojoClass.getClass().getDeclaredField("name"); System.out.println(field.getGenericType()); }
上述代码是通过反射机制用来获取类中name属性的类型,若不存在就会抛出异常。
调用方式:
MyClass myClass2 = new MyClass(); myClass2.getClassField(MyClass2.class);
传递时,指定的类,后面使用.class标识。除此之外,还可以用在构造方法上,例如:
public class MyClass2 { private Class<?> entityClass; public MyClass2(Class<?> entityClass) { this.entityClass = entityClass; } }
调用:
MyClass2 myClass22 = new MyClass2(String.class);
在创建对象时传入指定类型。
原文:https://www.cnblogs.com/zys2019/p/14841044.html