《Java基础知识》Java 泛型详解
JDK 1.5 之后,Java 通过泛型解决了容器类型安全这一问题,而几乎所有人接触泛型也是通过Java的容器。那么泛型究竟是什么?
泛型的本质是参数化类型;也就是说,泛型就是将所操作的数据类型作为参数的一种语法。
先对比一下有泛型和无泛型的写法。
无泛型
public class Dog{
String name;
int age;
/**
* 带参构造函数
* @param name
* @param age
*/
public Dog(String name,int age){
this.age = age;
this.name = name;
}
public void ptint(){
System.out.println("name = "+this.name+";age = "+age);
}
}
public class Node {
private Object obj;
public Object get(){
return obj;
}
public void set(Object obj){
this.obj=obj;
}
public static void main(String[] argv){
Dog dog=new Dog("花花",4);
Node node=new Node();
node.set(dog);
Dog dog2=(Dog)node.get();
dog2.ptint();
}
}
运行结果:

使用泛型
public class Node<T> {
private T obj;
public T get(){
return obj;
}
public void set(T obj){
this.obj=obj;
}
public static void main(String[] argv){
Dog dog=new Dog("花花",4);
Node<Dog> node=new Node();
node.set(dog);
Dog dog2=node.get();
dog2.ptint();
}
}
分析:
第一种:通过Object 类接收,最后使用的时候需要强制转换后才能正常使用,并且强转是否正确,只有在运行时才能知道是否正确。
第二种:通过 T 接收,在使用的使用<> 中指定本次使用具体类,可以很好的做到要用什么传什么,并且改写法,可以在编译的时候就发现类型不匹配问题。T接收,代表是Object类,所以可以传如任何对象。
在上述 Node 类中,我们假设需要使用使用传入类Dog的方法。显然obj.ptint() 是不行,obj能用只有Object的方法。那要如何使用呢?
思路是:让T不再代表Object,而是我指定的子类或者父类。
案例:
public class Node<T extends Dog> {
private T obj;
public T get(){
return obj;
}
public void set(T obj){
this.obj=obj;
}
public void printNode(){
obj.ptint();
}
public static void main(String[] argv){
Node<Dog> node=new Node<>();
node.set(new Dog("花花",4));
node.printNode();
}
}
运行结果:

到这里,已经感受到泛型的美好:那就是方便。
注意:泛型传递的时候是不变的。
案例:
public class Dog1 extends Dog {
/**
* 带参构造函数
*
* @param name
* @param age
*/
public Dog1(String name, int age) {
super(name, age);
}
}
public class PublicTest {
public static void main(String[] args) {
Node<Dog> node0 = new Node<>();
node0.set(new Dog("花花",4));
// Node<Dog1> node0 = new Node<>(); 是会编译报错的
main1(node0);
}
public static void main1(Node<Dog> node) {
node.printNode();
}
}
运行结果:

如果将上述代码的:Node<Dog> node0 = new Node<>(); 改成被注释的代码。会编译报错。也就是泛型是Node<Dog>只能传 Node<Dog>, 计算Dog1是Dog的子类也不行。上述写死的方式实在难受,所以Java也提供了解决方式。
案例:
public static void main(String[] args) {
Node<Dog1> node0 = new Node<>(); //不会编译报错,并且可以正常执行
node0.set(new Dog1("花花",6));
main1(node0);
}
public static void main1(Node<? extends Dog> node) {
node.printNode();
}
运行结果:

上述案例中mian1方法的入参:Node<? extends Dog> node 。这样就允许传入子类了。
这个时候我们再考虑一下,既然有向下泛型,有没有向上泛型呢,答案是有的:mian1方法的入参:Node<? extends Dog> node 中的extends 改成supper就可以了。
这个案例,大家自己动手试试。
案例:
public class Node<T> {
private T obj;
public T get(){
return obj;
}
public void set(T obj){
this.obj=obj;
}
public void printNode(){
System.out.println("只能使用Object方法");
}
}
public class PublicTest {
public static void main(String[] args) {
Node<Dog1> node0 = new Node<>(); //不会编译报错,并且可以正常执行
node0.set(new Dog1("花花",6));
main1(node0);
}
public static void main1(Node<?> node) {
node.printNode();
}
}
运行结果:

上述案例:支持传入向上泛型和向下泛型,不建议使用。 因为这类泛型回到一个问题,Node<T> 类中只能使用Object 的几个方法,局限很大。
泛型使用中的注意点。
案例:
import java.util.concurrent.ThreadLocalRandom;
public class PublicTest {
static <T> T[] toArray(T... args) {
return args;
}
static <T> T[] pickTwo(T a, T b, T c) {
switch(ThreadLocalRandom.current().nextInt(3)) {
case 0: return toArray(a, b);
case 1: return toArray(a, c);
case 2: return toArray(b, c);
}
throw new AssertionError(); // Can't get here
}
public static void main(String[] args) {
String[] attributes = pickTwo("Good", "Fast", "Cheap");
}
}
运行结果:

分析:上述代码第一感觉返回回来的是一个String[] 数组才对,为什么会出现类型转换错误。文章中已经提到:T 会被表示成Object。所以返回回来的数组Java也会处理成Object[] ,Object[] 是不能转换成 String[] 的。
既然知道原因了:我们试着修改一下代码:将T继承到String。这样java返回数组就会处理成String[]。
import java.util.concurrent.ThreadLocalRandom;
public class PublicTest {
static <T extends String> T[] toArray(T... args) {
return args;
}
static <T extends String> T[] pickTwo(T a, T b, T c) {
switch(ThreadLocalRandom.current().nextInt(3)) {
case 0: return toArray(a, b);
case 1: return toArray(a, c);
case 2: return toArray(b, c);
}
throw new AssertionError(); // Can't get here
}
public static void main(String[] args) {
String[] attributes = pickTwo("Good", "Fast", "Cheap");
System.out.println(attributes[0]+" : "+attributes[1]);
}
}
运行结果:

基类劫持:
案例:
public interface Node<T> {
T play();
}
public class Dog implements Node<Integer> {
@Override
public Integer play() {
return null;
}
}
public class Dog1 extends Dog implements Node<String> {
//...
}
编译报错:因为基类已经实现 Node<Integer> 那么子类也只能实现 Node<Integer>。
Java泛型中的标记符含义:
E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的java类型
S、U、V - 2nd、3rd、4th types 和T的作用一样。
总结:泛型给我们带来了方便,同时也要注意他的特点和用法。
参考资料:
https://www.cnblogs.com/dengchengchao/p/9717097.html
《Java基础知识》Java 泛型详解的更多相关文章
- java 泛型详解(普通泛型、 通配符、 泛型接口)
java 泛型详解(普通泛型. 通配符. 泛型接口) JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能---- ...
- java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 1. 概述 泛型在 ...
- Java泛型详解(转)
文章转自 importNew:Java 泛型详解 引言 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用.本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理 ...
- 【转】java 泛型详解
java 泛型详解 对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 ...
- 【转载】Java泛型详解
[转载]http://www.importnew.com/24029.html 对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考 ...
- java基础(十二 )-----Java泛型详解
本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...
- Java基础11:Java泛型详解
本文对java的泛型的概念和使用做了详尽的介绍. 本文参考https://blog.csdn.net/s10461/article/details/53941091 具体代码在我的GitHub中可以找 ...
- java 泛型详解-绝对是对泛型方法讲解
Reference: http://blog.csdn.net/s10461/article/details/53941091 1. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模 ...
- Java泛型详解,史上最全图文详解!
泛型在java中有很重要的地位,无论是开源框架还是JDK源码都能看到它. 毫不夸张的说,泛型是通用设计上必不可少的元素,所以真正理解与正确使用泛型,是一门必修课. 一:泛型本质 Java 泛型(gen ...
- 笔记-java泛型详解
首先,先说明一下,java泛型文章的出处:http://www.cnblogs.com/lzq198754/p/5780426.html 作为学习笔记保存. 1.为什么需要泛型 泛型在Java中有很重 ...
随机推荐
- ubuntu server 1604 搭建FTP服务器
1.查看是否安装 ftp服务器vsftpd -v 2.安装ftp服务器sudo apt-get install vsftpd 3.如果安装失败或者配置出现问题,可以卸载 ftp服务器sudo apt- ...
- Docker (一) 安装 Oracle18c
通过Docker 安装 Oracle18c 1.拉取 oracle18c 镜像 docker pull registry.cn-hangzhou.aliyuncs.com/zhengqing/orac ...
- C#笔记01——注释、进制、基本数据类型、量和输入输出函数
一.注释 1.单行注释 使用方法:行首加 ” //“: VS2019中的快捷键(以后如果不特加说明都是VS2019): 注释(CTRL+E,C): 取消注释(CTRL+E,U): 2.多行注释 使用方 ...
- 附011.Kubernetes-DNS及搭建
一 Kubernetes DNS介绍 1.1 Kubernetes DNS发展 作为服务发现机制的基本功能,在集群内需要能够通过服务名对服务进行访问,因此需要一个集群范围内的DNS服务来完成从服务名到 ...
- MySQL5.6.36 自动化安装脚本
背景 很好的朋友邱启明同学,擅长MySQL,目前任职某大型互联网业MySQL DBA,要来一套MySQL自动安装的Shell脚本,贴出来保存一些. 此版本为 MySQL 5.6.365 ###### ...
- Spring Boot整合MyBatis(非注解版)
Spring Boot整合MyBatis(非注解版),开发时采用的时IDEA,JDK1.8 直接上图: 文件夹不存在,创建一个新的路径文件夹 创建完成目录结构如下: 本人第一步习惯先把需要的包结构创建 ...
- javaSE笔记(重点部分)
Java 基础篇 数据类型 基本数据类型 由于java是强类型语言,所以要进行有些运算的时候,需要用到类型转换. 低-----------------------------高 byte,short, ...
- RestTemplate常用的get和post带参数请求
在RestTemplate提供的方法中,有一个参数就是目标URL,参数是跟在后面的一个数量可变参数,但是在这里就有个问题,这个方法怎么知道我传的参数值是对应在目标接口的哪个参数的呢: public & ...
- MyCat学习 ------分库分表 随笔
垂直切分.水平切分 1.垂直分库,解决库中表太多的问题. 2.垂直分表,解决表中列太多的问题.例如 商品表 包含 产地.二维码 .时间.价格.各个列.分为不同的小表. 水平切分, 大数据表拆分为小表 ...
- CryptoJS中WordArray
目录 WordArray (An array of 32-bit words. 1.1 : 初始化 1.2 : WordArray 对象 ->16进制字符串 1.3 : 16进制字符串 -> ...