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. [Odoo12基础教程]之win10中odoo12环境搭建

    所需材料 1.python3.7 2.pycharm社区版及以上 3.postgresSQL10 下载链接:https://www.enterprisedb.com/thank-you-downloa ...

  2. 快速入门函数式编程——以Javascript为例

    函数式编程是在不改变状态和数据的情况下使用表达式和函数来编写程序的一种编程范式.通过遵守这种范式,我们能够编写更清晰易懂.更能抵御bug的代码.这是通过避免使用流控制语句(for.while.brea ...

  3. ehcache监控

    引入Maven依赖 <dependency> <groupId>org.terracotta.ehcachedx.com.javabi</groupId> < ...

  4. day29 文件的上传和下载 socketserver(并发)

    文件上传的讲解: import subprocess res=subprocess.Popen("dir", shell=True, stderr=subprocess.PIPE, ...

  5. 设计模式之代理模式--PHP

    代理模式是常用的设计模式之一,代理模式为对象的间接访问提供了一套方案,可以对对象访问进行控制,也能监控对象访问相关的数据信息. 代理模式(Proxy)就是给某一个对象提供代理,在由代理控制原对象的访问 ...

  6. bs4-爬取小说

    bs4 bs4有两种运行方式一种是处理本地资源,一种是处理网络资源 本地 from bs4 import BeautifulSoup if __name__ == '__main__': fr = o ...

  7. c# 窗体开发3 文件处理技术

    以字节形式向磁盘写入数据通常称为字节流(比特流) 常常使用System.Io 常用的类 类 说明 File 提供用于创建.复制.删除.移动和打开文件的静态方法,并协助创建 FileStream 对象. ...

  8. elastic search(es)安装

    全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引擎的首选. 它可以快速地储存.搜索和分析海量数据.维基百科.Stack Overflow.Gi ...

  9. Spring Boot SpringApplication启动类(二)

    目录 前言 1.起源 2.SpringApplication 运行阶段 2.1 SpringApplicationRunListeners 结构 2.1.1 SpringApplicationRunL ...

  10. 转:SSL 握手协议详解

    SSL 握手协议详解 RSA作为身份认证,ECDHE来交换加密密钥,AES/DES等作为加密. 如果RSA来加解密,那么身份认证后,直接用认证后的RSA公钥解密.不需要再额外交换加密密钥了. 相关报文 ...