泛型
泛型,即“参数化类型”.
泛型,将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在调用时传入具体的类型(类型实参)。
泛型的好处
-
类型安全,消除强制类型转换。 泛型消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。
-
减少重复代码。 提高代码的重用率,同样的逻辑,使用泛型后,能够支持多种类型,不同类型不用写重复代码。
Java泛型中的标记符含义:
E - Element (元素,在集合中使用)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的java类型
S、U、V - 2nd(第二)、3rd(第三)、4th(第四) 类型
List集合的泛型
比如,最常见的List集合,List常用方法的源码如下:
在List接口中采用泛型定义之后, List<E>中的E表示类型形参,声明List并指定类型E后,凡是出现E的地方均表示相同的类型(类型实参)。
public interface List<E> extends Collection<E> {
boolean add(E e);
E get(int index);
}
示例如下:
public class ListTest {
public static void main(String[] args) {
//声明集合list的泛型<E>为 String类型
List<String> list = new ArrayList<>();
list.add("ab");
list.add("cd");
//由于list指定了泛型是String,那么add(E e)方法的参数E只能是String类型,添加整数到该集合会报错
// list.add(123);
//由于 list 指定了泛型是 String,调用get方法直接得到String,而不需要做类型转换
String first = list.get(0);
}
}
泛型类、泛型接口和泛型方法
泛型的常见用法有 泛型类、泛型接口、泛型方法。
上面的 interface List<E> 就是一个典型的泛型接口。
泛型类跟泛型接口类似,也可以在类后面声明泛型。
泛型类
示例如下:
/**
* 泛型类, User<T>定义了泛型类型为T,那么变量T data和方法T getData()中的 T类型也得跟定义的类型一样。
*
* @param <T>
*/
public class User<T> {
private T data;
public User(T data) {
this.data = data;
}
public User() {
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static void main(String[] args) {
//User<Integer>定义了泛型类型为Integer, 那么data就必须是 Integer
User<Integer> user1 = new User<>();
user1.setData(10);
System.out.println(user1.getData());
//User<String>定义了泛型类型为 String, 那么data就必须是 String
User<String> user2 = new User<>();
user2.setData("Rui");
System.out.println(user2.getData());
}
}
泛型方法
泛型方法的格式如下:
修饰符 <声明泛型的类型> 返回的类型 方法名称();
比如:
public <T> void getUserName(T t) {}
在返回类型的前面声明 泛型类型 <T> ,那么方法的所有 T 类型与声明的类型一样。
如果需要限制 T的类型为 继承User的类,可以使用 T extends User,如下所示:
public <T extends User> void getUserName(T t) {
}
如果是多个泛型,则用逗号隔开,如:
public <T,M> String getUserName(T t, M m) {}
示例如下:
public class Book {
/**
* 泛型方法
* @param t 泛型对应的参数
* @param <T> 泛型的类型
* @return
*/
public <T> String getName(T t){
return t.toString();
}
/**
* 存在多个泛型的泛型方法
* @param t 第一个泛型对应的参数
* @param m 第二个泛型对应的参数
* @param <T> 第一个泛型的类型
* @param <M> 第二个泛型的类型
* @return
*/
public <T,M> String getMergeName(T t, M m) {
return t.toString() + m.toString();
}
public static void main(String[] args) {
Book book = new Book();
System.out.println(book.getName("abc"));
System.out.println(book.getName(123));
System.out.println(book.getName(book));
System.out.println(book.getMergeName("abc", 123));
}
}
类型通配符
类型通配符?的含义如下:
<?extends T>表示有上限的通配符,能接受其类型和其子类的类型, 此时 T 即泛型类型的上边界
<?super T> 表示有下限的通配符,能接受指定类型及其父类类型,此时 T 即泛型类型的下边界
在List集合中,就有大量的类型通配符的应用。
boolean addAll(Collection<? extends E> c);
void sort(Comparator<? super E> c);
类型通配符的使用,如下:
public class GenericTest {
class Father {
}
class Son extends Father {
}
/**
* 使用了类型通配符,只允许 继承了Father的类作为泛型的类型
* @param list
*/
public static void select(List<? extends Father> list) {
}
public static void main(String[] args){
List<Father> list1 = new ArrayList<>();
List<Son> list2 = new ArrayList<>();
List<String> list3 = new ArrayList<>();
select(list1);
select(list2);
//list3的泛型为 String,并没有继承 Father,编译会报错: Required type: List <? extends Father>
// select(list3);
}
}
-
类型通配符与泛型方法的区别:
类型通配符既可以在方法签名处定义形参的类型, 也可以用于定义变量的类型;
但是泛型方法中的泛型形参必须在对应方法中声明.
参考资料:
https://www.cnblogs.com/lwbqqyumidi/p/3837629.html