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 泛型详解的更多相关文章

  1. java 泛型详解(普通泛型、 通配符、 泛型接口)

    java 泛型详解(普通泛型. 通配符. 泛型接口) JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能---- ...

  2. java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一

    对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 1. 概述 泛型在 ...

  3. Java泛型详解(转)

    文章转自  importNew:Java 泛型详解 引言 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用.本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理 ...

  4. 【转】java 泛型详解

    java 泛型详解 对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 ...

  5. 【转载】Java泛型详解

    [转载]http://www.importnew.com/24029.html 对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考 ...

  6. java基础(十二 )-----Java泛型详解

    本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...

  7. Java基础11:Java泛型详解

    本文对java的泛型的概念和使用做了详尽的介绍. 本文参考https://blog.csdn.net/s10461/article/details/53941091 具体代码在我的GitHub中可以找 ...

  8. java 泛型详解-绝对是对泛型方法讲解

    Reference:  http://blog.csdn.net/s10461/article/details/53941091 1. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模 ...

  9. Java泛型详解,史上最全图文详解!

    泛型在java中有很重要的地位,无论是开源框架还是JDK源码都能看到它. 毫不夸张的说,泛型是通用设计上必不可少的元素,所以真正理解与正确使用泛型,是一门必修课. 一:泛型本质 Java 泛型(gen ...

  10. 笔记-java泛型详解

    首先,先说明一下,java泛型文章的出处:http://www.cnblogs.com/lzq198754/p/5780426.html 作为学习笔记保存. 1.为什么需要泛型 泛型在Java中有很重 ...

随机推荐

  1. HTML的footer置于页面最底部

    vue项目中,使用element-ui的布局,仍然出现footer不固定页面底部的情况,网上找到的一个管用的 方法是:footer高度固定+绝对定位 <html> <head> ...

  2. mac软件推荐及chrome插件推荐

    通用软件 Alfred (超级好用的效率工具) 用mac这个软件一定要装,用习惯之后加上电脑本身的快捷键.效率提升的飞起. Alfred我常使用的功能有: 搜索chrome的书签 我搜索的书签大概分为 ...

  3. UCloud 云服务内容审核 Java 版本实现

    前言 最近不少小伙伴反映上传小黄图偶尔性的异常,并且不能上传动态图片,很是苦恼!无她,鉴黄API还没有这么智能,毕竟是自己训练的,不是那么专业!为了更好的服务广大网友,撸主决定接入更加智能快速的鉴黄服 ...

  4. Python文件和数据格式化(教程)

    文件是一个存储在副主存储器的数据序列,可包含任何数据内容. 概念上,文件是数据的集合和抽象,类似的,函数是程序的集合和抽象. 用文件形式组织和表达数据更有效也更加灵活. 文件包括两种形式,文本文件和二 ...

  5. 使用three.js创建大小不随着场景变化的文字

    使用three.js创建大小不随着场景变化的文字,需要以下两步: 1.将文字绘制到画布上. 2.创建着色器材质,把文字放到三维场景中. 优点: 1.跟用html实现文字相比,这些文字可以被模型遮挡,更 ...

  6. Mybatis拦截器实现原理深度分析

    1.拦截器简介 拦截器可以说使我们平时开发经常用到的技术了,Spring AOP.Mybatis自定义插件原理都是基于拦截器实现的,而拦截器又是以动态代理为基础实现的,每个框架对拦截器的实现不完全相同 ...

  7. 分布式存储Minio集群环境搭建

    MinIO 分布式集群搭建 分布式 Minio 可以让你将多块硬盘(甚至在不同的机器上)组成一个对象存储服务.由于硬盘分布在不同的节点上,分布式 Minio 避免了单点故障. Minio 分布式模式可 ...

  8. MySQL统计各个表中的记录数

    通过下面的SQL语句可以统计出数据库的各个表中的记录数: select table_schema, table_name,table_rows from information_schema.tabl ...

  9. 【Android - 问题解决】之Android Studio在Refreshing Gradle Project卡住的问题

    今天把Android Studio从2.2版本升级到了2.3版本,但是再打开以前项目的时候,就卡在 “Refreshing XXX Gradle Project” 这里不动了. 网上搜了一些解决方案, ...

  10. Java中我常用到的十二个最基本的快捷键

    自己在Java中经常用到的快捷键先记下来方便日后学习之用: 1. Ctrl+shift+r:打开资源 这可能是所有快捷键组合中最省时间的了.这组快捷键可以让你打开你的工作区中任何一个文件,而你只需要按 ...