Java泛型T,E,K,V,N,?与Object区别和含义

作者:不想起床的小张 时间:2022-02-23 07:25:30 

通常我们在看一些源码时,发现全是T、?,晕乎乎的:sob:。于是,把泛型掌握好十分重要!

Java泛型T,E,K,V,N,?与Object区别和含义

什么是泛型

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;
   }

@Override
   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;

}

@Override
   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;
   }

@Override
   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;

}

@Override
   public String toString() {
       return "User{" +
               "id=" + id +
               ", name='" + name + '\'' +
               '}';
   }
}

class Phone implements Serializable {

Double price;

String color;

@Override
   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允许被传入,否则会编译报错

Java泛型T,E,K,V,N,?与Object区别和含义

? super T

这是**泛型下边界:**只有T对象的父类可以被传入

如果是? super D,那么只有C和A允许被传入,否则会编译报错

Java泛型T,E,K,V,N,?与Object区别和含义

T 和 ?

不知道看到这里,有没有疑惑。T和?好像作用差不多啊,有什么区别?

这里解释一下,T一般作为泛型参数,而?是更多是用来一个不确定的引用类型,意会一下吧~~~

Java泛型T,E,K,V,N,?与Object区别和含义

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作为泛型来使用时,不仅写起来麻烦,还要不停的进行类型转化,还很容易出现问题,很逊的诶~

举个例子看看:

Java泛型T,E,K,V,N,?与Object区别和含义

利用Object定义了一个数字变量,我们常识将其向下转型为Integer和String。将一个数字转型为字符串是一件荒唐的事情,可编译器并不能察觉这件事,直到程序运行了起来...

Java泛型T,E,K,V,N,?与Object区别和含义

类型转换异常!!!

来源:https://juejin.cn/post/7072703705811779620

标签:Java,泛型,Object
0
投稿

猜你喜欢

  • Spring Boot与Spark、Cassandra系统集成开发示例

    2021-06-03 13:40:51
  • Android隐藏顶部状态栏所遇到的问题

    2023-08-02 03:39:26
  • Java最长公共子序列示例源码

    2023-08-20 13:25:37
  • Android使用TextView跑马灯效果

    2022-05-20 20:07:20
  • C#深拷贝方法探究及性能比较(多种深拷贝)

    2022-08-30 18:17:02
  • Json字符串与Object、List、Map的互转工具类

    2023-04-30 16:08:16
  • Android编程中File文件常见存储与读取操作demo示例

    2021-11-24 18:33:20
  • PageHelper插件实现一对多查询时的分页问题

    2021-11-05 07:02:34
  • java ThreadLocal使用案例详解

    2022-02-01 05:14:57
  • Java ByteBuffer网络编程用法实例解析

    2022-09-17 20:16:22
  • java 深拷贝与浅拷贝机制详解

    2023-02-18 19:00:59
  • springboot整合Quartz实现动态配置定时任务的方法

    2023-03-08 22:13:10
  • Java实现动态模拟时钟

    2022-07-25 17:35:25
  • Java编程BigDecimal用法实例分享

    2022-05-02 05:40:06
  • Android实现系统重新启动的功能

    2021-06-04 02:48:56
  • Android网络判断知识小结

    2022-06-01 02:39:16
  • java设计模式之工厂方法模式

    2022-08-29 05:14:10
  • Android实现上下菜单双向滑动

    2023-06-10 02:43:37
  • 基于C#实现XML文件读取工具类

    2021-10-07 07:42:19
  • SpringBoot整合XxlJob分布式任务调度平台

    2022-07-09 09:47:37
  • asp之家 软件编程 m.aspxhome.com