Java泛型T、E、K、V、N、?和Object区别和涵义
通常我们在看一些源码时,发觉满是T、?,晕乎乎的:sob:。于是,把泛型把握好十分紧张!
什么是泛型
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时典范宁静检测机制,该机制允许步骤员在编译时检测到不法的典范。
泛型的实质是参数化典范,也就是说所利用的数据典范被指定为一个参数。
泛型有什么利益?写个例子一清二楚:
我们要封装一个消息呼应类:
public class Result implements Serializable {
// 呼应码
Integer code;
// 对否告捷
Boolean success;
// 前往体数据
User user;
public Result(Integer code, Boolean success, User user) {
this.code = code;
this.success = success;
this.user = user;
}
public String toString() {
return "Result{" +
"code=" + code +
", success=" + success +
", user=" + user +
'}';
}
public static void main(String[] args) {
User user = new User(1, "Tony");
Result result = new Result(200, true, user);
System.out.println(result);
}
}
class User implements Serializable {
Integer id;
String name;
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
Result{code=200, success=true, user=User{id=1, name='Tony'}}
历程已完毕,退去代码0
呼~如此这个反响体就可以前往哀求形态和用户信息了。可如今需求又必要前往关于手机的信息,那我们又得封装一个能前往手机信息的呼应类了...到后方另有衣服、鞋子...那不得累死?这时分泛型下场了:
public class Result<T> implements Serializable {
// 呼应码
Integer code;
// 对否告捷
Boolean success;
// 前往体数据
T data;
public Result(Integer code, Boolean success, T data) {
this.code = code;
this.success = success;
this.data = data;
}
public String toString() {
return "Result{" +
"code=" + code +
", success=" + success +
", data=" + data +
'}';
}
public static void main(String[] args) {
User user = new User(1, "Tony");
Result<User> resultUser = new Result<>(200, true, user);
System.out.println(resultUser);
Phone phone = new Phone(999.99, "Yellow");
Result<Phone> resultPhone = new Result<>(200, true, phone);
System.out.println(resultPhone);
}
}
class User implements Serializable {
Integer id;
String name;
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
class Phone implements Serializable {
Double price;
String color;
public String toString() {
return "Phone{" +
"price=" + price +
", color='" + color + '\'' +
'}';
}
public Phone(Double price, String color) {
this.price = price;
this.color = color;
}
}
Result{code=200, success=true, data=User{id=1, name='Tony'}}
Result{code=200, success=true, data=Phone{price=999.99, color='Yellow'}}
历程已完毕,退去代码0
可见,使用泛型,可以一致标识必要前往的实体类。不管你来什么类,我都可以给你塞进入!
第一次交往约莫看不太明白,底下就具体解说
泛型办法
你可以写一个泛型办法,该办法在调用时可以吸收不同典范的参数。依据转达给泛型办法的参数典范,编译器得当地处理每一个办法调用。
语法例则
- 一切泛型办法声明都有一个典范参数声明局部(由尖括号分开),该典范参数声明局部在办法前往典范之前
好比说这是一个用来打印数组的泛型办法:
private static <E> void printArray(E[] inputArray)
- 每一个典范参数声明局部包含一个或多个典范参数,参数间用逗号离隔。一个泛型参数,也被称为一个典范变量,是用于指定一个泛型典范称呼的标识符。
好比这个办法
private static <E,T> void printArray(E[] inputArray, T data)
- 典范参数能被用来声明前往值典范,并且能作为泛型办法取得的实践参数典范的占位符。
- 泛型办法体的声明和其他办法一样。注意典范参数只能代表引用型典范,不克不及是原始典范(int double char等)
泛型标志符
- E Element 聚集元素
- T Type Java类
- K Key 键
- V Value 值
- N Number 数值典范
- ? 表现不确定的Java典范
这些标志并不是限定仅有对应的典范才干使用,即使你一致使用A-Z英笔墨母的此中一个,编译器也不会报错。之以是又不同的标志符,这是一种商定。在开发中很多端正都是一种商定,它能提高我们代码的可读性,便利团队见的互助开发
写个完备的例子:
public class TFunction {
public static void main(String[] args) {
// 创建种品种型的数组
Integer[] intArray = {1, 2, 3, 4, 5};
Double[] doubleArray = {1.1, 2.2, 3.3, 4.4};
Character[] charArray = {'H', 'E', 'L', 'L', 'O'};
System.out.println("整型数组元素为:");
printArray(intArray); // 转达一个整型数组
System.out.println("\n双精度型数组元素为:");
printArray(doubleArray); // 转达一个双精度型数组
System.out.println("\n字符型数组元素为:");
printArray(charArray); // 转达一个字符型数组
}
// 泛型办法
private static <E> void printArray(E[] inputArray) {
// 遍历打印数组
Arrays.stream(inputArray).forEach(e -> {
System.out.printf("%s ", e);
});
System.out.println();
}
}
泛型类
泛型类的声明与非泛型类几乎相反,唯一的不同在于类名的后方添加了参数声明局部
这边就不举例子了,由于开篇的例子就是封装了一个泛型类,事先不太了解的可以再回去看一下
典范通配符
?
我们寻常可以使用?来承接一切的引用典范,搬运一个菜鸟上的例子:
public class GenericTest {
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
getData(name);
getData(age);
getData(number);
}
public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
}
data :icon
data :18
data :314
?extends T
这是泛型上界限:仅有T目标的子类可以被传入
假如是? extends C,那么仅有D和E允许被传入,不然会编译报错
? super T
这是泛型下界限:仅有T目标的父类可以被传入
假如是? super D,那么仅有C和A允许被传入,不然会编译报错
T 和 ?
不晓得看到这里,有没有疑惑。T和?仿佛作用差不多啊,有什么区别?
这里表明一下,T寻常作为泛型参数,而?是更多是用来一个不确定的引用典范,意会一下吧~~~
T 和 Object
重头戏!!
晓得的Object的同道都了解其是Java的超类(一切目标的父类),不了解的可以去看看我的博客,有做具体的表明。
那么成绩就来了,Object仿佛可以代替泛型的功效啊!一切能用到泛型的场合Object都可以!
但是,在JDK5之前,都是用的Object,但其存在很多的成绩,JDK5之后便引入了泛型
Object是一切类的父类,在编码历程中就不免显现典范转化成绩,且在编译阶段不会报错,到了运转阶段才暴露成绩,大大低落了步骤的宁静性和健壮性!
举例之前说一些转型的分类:
- 向上转型 用父类声明一个子类目标 比如:Animal是Cat的父类,在声明时我们这么写:
Animal cat = new Cat();
- 向下转型
- 将父类目标强转为其子类实例:
Animal cat = new Cat();
Cat anotherCat = (Cat) cat;
以是当我们使用Object作为泛型来使用时,不仅写起来贫苦,还要不休的举行典范转化,还很容易显现成绩,很逊的诶~
举个例子看看:
使用Object界说了一个数字变量,我们知识将其向下转型为Integer和String。将一个数字转型为字符串是一件怪诞的事变,可编译器并不克不及发觉这件事,直到步骤运转了起来...
典范转换特别!!!
总结
泛型的显现,当典范转化显现成绩的时分,在编译阶段就会暴显露来。处理了Object存在的诸多成绩,让代码愈加优雅,步骤愈加宁静,愈加健壮。
作者:不想起床的小张
链接:
https://juejin.cn/post/7072703705811779620