什么是泛型

本质而言,指的是参数化类型。
参数化类型很重要,因为使用该特性创建的类,接口以及方法,可以作为参数指定所操作数据的类型。
上面的这个是名词解释,看了是不是一头雾水,跟天书一样。
风.fox
可以这么理解
规定了变量数据类型是数值型,你就必须传输数值型的,传输其他类型的就会错误。

class Box<T>{T ob;Box(T o){ob=o;}T getob{return ob;}public staic void main(String args[]){Box<Integer> test=new Box<Integer>(88);//  主要在这里,规定了 integer 类型,如果是其他类型就错误// 88 是数值类型,是正确的,符合要求// 如果 改为 "ABC" ,它是字符型的,不符合要求,必然报错Box<Integer> test=new Box<Integer>("ABC");//报错}
}

泛型形式

class Box<T>{

T是类型参数的名称(可以改变的,例如:A)。
这个名称是实际类型的站位符。
注意T 被包含在<>中。
T是将在创建Box对象时指定的实际类型的占位符。
因此 ob 是传递给T的那种实际类型的对象。
那么,开始的泛型,实际类型是 Integer

泛型只使用对象(Integer,Double,String,Object等等),不能使用基本类型(如:int或char)

如果要使用基本类型,可以使用类型封装器封装基本类型

基于不同类型参数的泛型类型是不同的

带两个或更多类型参数的泛型类

泛型中可以声明多个类型参考。
为了指定两个或更多个类型参数,只需要使用逗号分隔参数列表既可以。

class Box<T,V>{T ob1;V ob2;Box(T o1,V o2){ob1=o1;ob2=o2;}T getob1(){return ob1;}V getob2(){return ob2;}void show(){System.out.println(ob1.getClass().getName());System.out.println(ob2.getClass().getName());}public static void main(String args[]){Box<Integer,String> my=new Box<Integer,String>(88,"fox");my.show();int v=my.ob1();String str=my.ob2();System.out.println(v);System.out.println(str);}
}

命名规则

类型参数的命名有一套默认规则,为了提高代码的维护性和可读性,强烈建议遵循这些规则。JDK中,随处可见这些命名规则的应用。
E - Element (通常代表集合类中的元素)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. – 第二个,第三个,第四个类型参数……
注意,父类定义的类型参数不能被子类继承。

有界类型或通配符

public void boxTest(Box<Number> n){  ……
}

该方法只能接受Box这一种类型的参数,当我们输入一个Box或者Box时会报错,尽管Integer与Double是Number的子类。可是如果我们希望该方法可以接受Number以及它的任何子类,该怎么办呢?
这时候就要用到通配符了,改写如下:

public void boxTest(Box<? extends Number> n){  ……
}

“? extends Number”就代表可以接受Number以及它的子类作为参数。这种声明方式被称为上限通配符(upper bounded wildcard)。
相反地,如果我们希望该方法可以接受Integer,Number以及Object类型的参数怎么办呢?应该使用下限通配符(lower bounded wildcard):

public void boxTest(Box<? super Integer> n){  ……
}  

“? super Integer”代表可以接受Integer以及它的父类作为参数。

如果类型参数中既没有extends 关键字,也没有super关键字,只有一个?,代表无限定通配符(Unbounded Wildcards)。
通常在两种情况下会使用无限定通配符:
(1)如果正在编写一个方法,可以使用Object类中提供的功能来实现
(2)代码实现的功能与类型参数无关,比如List.clear()与List.size()方法,还有经常使用的Class

public static void printList(List<Object> list) {  for (Object elem : list)  System.out.println(elem + "");  System.out.println();
} 

该方法只能接受List型的参数,不接受其他任何类型的参数。但是,该方法实现的功能与List之中参数类型没有关系,所以我们希望它可以接受包含任何类型的List参数。代码改动如下:

public static void printList(List<?> list) {  for (Object elem : list)  System.out.println(elem + " ");  System.out.println();
} 
需要特别注意的是,List<?>与List<Object>并不相同,无论A是什么类型,List<A>是List<?>的子类,但是,List<A>不是List<Object>的子类。

例如:
List lb = new ArrayList<>();
List la = lb; // 会报编译错误,尽管Integer是Number的子类,但是List不是List的子类
List与List的关系如下:

所以,下面的代码是正确的:

List<? extends Integer> intList = new ArrayList<>();
List<? extends Number>  numList = intList;  // 不会报错, List<? extends Integer> 是 List<? extends Number>的子类  

下面这张图介绍了上限通配符、下限通配符、无限定通配符之间的关系:

编译器可以通过类型推断机制来决定通配符的类型,这种情况被称为通配符捕获。大多时候我们不必担心通配符捕获,除非编译器报出了包含“capture of”的错误。例如:
编译器可以通过类型推断机制来决定通配符的类型,这种情况被称为通配符捕获。大多时候我们不必担心通配符捕获,除非编译器报出了包含“capture of”的错误。例如:

public class WildcardError {  void foo(List<?> i) {  i.set(0, i.get(0));  //会报编译错误
}
} 

上例中,调用List.set(int,E)方法的时候,编译器无法推断i.get(0)是什么类型,就会报错。
我们可以借助一个私有的可以捕获通配符的helper方法来解决这种错误:

public class WildcardFixed {  void foo(List<?> i) {  fooHelper(i);  }  // 该方法可以确保编译器通过通配符捕获来推断出参数类型  private <T> void fooHelper(List<T> l) {  l.set(0, l.get(0));  }  } 

按照约定俗成的习惯,helper方法的命名方法为“原始方法”+“helper”,上例中,原始方法为“foo”,所以命名为“fooHelper”

关于什么时候该使用上限通配符,什么时候该使用下限通配符,应该遵循一下几项指导规则。
首先将变量分为in-变量与out-变量:in-变量持有为当前代码服务的数据,out-变量持有其他地方需要使用的数据。例如copy(src, dest)方法实现了从src源头将数据复制到dest目的地的功能,那么src就是in-变量,而dest就是out-变量。当然,在一些情况下,一个变量可能既是in-变量也是out-变量。
(1)in-变量使用上限通配符;
(2)out-变量使用下限通配符;
(3)当in-变量可以被Object类中的方法访问时,使用无限定通配符;
(4)一个变量既是in-变量也是out-变量时,不使用通配符
注意,上面的规则不适用于方法的返回类型。

擦除(擦拭)

编译JAVA代码时,所有泛型信息被移除(擦除)。
这意味着使用它们的界定类型替换类型参数,如果没有显式地指定界定类型,就使用Object然后应用适当的类型转换(根据类型参数而定),以保持与类型参数所指定类型的兼容性。
编译器偶尔需要为类添加桥接方法,用于处理如下情形:子类中重写方法的类型擦除不能产生与超类方法相同的擦除。对于这种情况,会生成使用超类类型擦除的方法,并且这个方法调用具有子类指定类型擦除方法。
这里让我了解 擦除和桥接原理

class Gen<T>{T ob;Gen(T o){ob=o;}T getob(){return ob;}
}
class Gen2 extends Gen<String>{Gen2(String o){super(o);}String getob(){return ob;}public static main(String args[]){Gen2 strob2=new Gen2("Test");System.out.println(strob2.getob());}
}

上面是我们自己编写的类。编译器会帮我们自动生成一个桥接方法【如下】。

class Gen2 extends Gen<String>{Gen2(String o){super(o);}String getob(){return ob;}//下面这个 是编译器 自动添加的桥接方法Object getob(){return ob;}public static main(String args[]){Gen2 strob2=new Gen2("Test");System.out.println(strob2.getob());}
}

使用泛型的一些限制

不能实例化类型参数

class Gen<T>{T ob;Gen(){ob= new T();}
}

试图创建T的实例,这是非法的。T在运行时不存在,编译器如何知道创建哪种类型的对象?
编译过程中,擦除了所有类型参数

对静态成员的一些限制

静态成员不能使用在类中声明的类型参数

class Gen<T>{static T ob;//错误static T Gen(){//错误return ob;}
}

尽管不能声明某些静态成员,他们使用有类型声明的类型参数,
但是可以声明静态的泛型方法,这种方法可以定义他们自己的类型参数。

对泛型数组的一些限制

不能实例化元素类型为类型参数的数组
不能创建特定类型的泛型引用数组。

对泛型异常的限制

泛型类不能扩展 Throwable,这意味着不能创建泛型异常类

部分来自
http://blog.csdn.net/u012152619/article/details/47253811

JAVA 泛型 入门相关推荐

  1. java 泛型应用,通用返回类,泛型方法,泛型静态方法

    java 泛型应用,通用返回类,泛型方法,泛型静态方法 泛型简介 应用示例 静态方法增加泛型参数 调用静态公有方法 对比调用非静态公有方法(成员方法) 泛型简介 这里不多说明,详见 java泛型入门 ...

  2. Java泛型进阶篇: 无界通配符、上界通配符以及下界通配符

    专栏文章导航 Java泛型入门篇: 泛型类.泛型接口以及泛型方法 Java泛型进阶篇: 无界通配符.上界通配符以及下界通配符 Java泛型原理篇: 类型擦除以及桥接方法 文章目录 前言 1. 无界通配 ...

  3. 大白话说Java泛型:入门、使用、原理

    文章首发于[博客园-陈树义],点击跳转到原文<大白话说Java泛型:入门.使用.原理> 远在 JDK 1.4 版本的时候,那时候是没有泛型的概念的.当时 Java 程序员们写集合类的代码都 ...

  4. java 泛型 默认值_java泛型 Java泛型的入门知识

    在学习java的过程当中,我们就肯定会接触到java泛型,那么你知道java泛型是什么吗,有关java的使用方法又有哪些呢?今天小编就从java泛型的使用方法来了解一下java泛型这一知识. java ...

  5. java泛型 之 入门(interface)

    一:泛型简单介绍: (1)所谓泛型,就是变量类型的參数化. 泛型是JDK1.5中一个最重要的特征.通过引入泛型,我们将获得编译时类型的安全和执行时更小的抛出ClassCastException的可能. ...

  6. 聊一聊Java 泛型中的通配符 T,E,K,V,?

    点击上方"方志朋",选择"设为星标" 回复"1024"获取独家整理的学习资料 作者:glmapper juejin.im/post/5d57 ...

  7. Java从入门到精通十一(javaIO流)

    Java从入门到精通十一(javaIO流) IO引入 字节流和字符流(一些说明) 字节流没有用到缓冲区并不是没有用到内存 为什么输入流是读数据,而输出流是写数据? 字节流说明 字节输入流读数据的方法 ...

  8. Java 基础入门,小白提升路线图

    1000+最新Java面试题 获取学习路线资料啦 Java的基础知识就像我们所住的房子的地基,如果地基不稳,上面所盖的楼房再宏伟,也是没人敢去入住的,同理Java的基础不牢固,以后也很难成为真正意义上 ...

  9. java新手入门学习指南

    java新手入门学习指南由 android杂谈网原创. 0.准备 1.JDK安装和环境变量配置 2. 安卓开发环境搭建 3.熟悉eclipse开发环境,并使用eclipse创建第一个java应用程序 ...

  10. Java从入门到精通十七(Stream 流)

    Java从入门到精通十七(Stream 流 ) Stream 流引入说明 Stream 流的常用方法说明 1: filter() 2:collect() 3:distinct() 4:sorted() ...

最新文章

  1. Uploadify v3.2.1
  2. 强制修改mysql 中root的密码
  3. python里面对文件的读写操作
  4. nova7修屏逛校园2021-07-07
  5. pl0源码(可在delphi7中运行)
  6. Oracle Enterprise Manager简介
  7. java 二进制 base64编码_java 按字节读写二进制文件(Base64编码解码)
  8. 工业POE供电交换机在安防应用中的优势有哪些?
  9. angular6--创建项目
  10. 【四】Jmeter:逻辑控制器
  11. java实现使用JDBC-ODBC桥操作数据库。
  12. vue cli3--创建通用模版
  13. mfc radio group 设置
  14. java中关于x轴翻转和y轴翻转如何计算_如何避免这8个常见的深度学习/计算机视觉错误?
  15. chm文件打开出现已取消该网页的导航
  16. Hyper-V 的用途与安装
  17. 使用MMD模型通过Kivicube平台制作WebAR与小程序AR
  18. 乐鑫开发工具链之国内镜像gitee飞起
  19. 灵魂拷问:为什么 Java 字符串是不可变的?
  20. 混合云风头正劲 青云QingCloud为何成为领导者?

热门文章

  1. 解决错误---undefined reference to `pthread_create‘
  2. c#中抽象类和接口异同
  3. oracle根据当前月份往前,Oracle SQL - 根据月份对时间序列分区求和
  4. 拓端tecdat|R语言ggsurvplot绘制生存曲线报错 : object of type ‘symbol‘ is not subsettable
  5. 案例4-3 朋友圈 (25 分)(并查集)
  6. linux c语言头文件 在另外的地方,linux下的c语言的头文件在windows下头文件是哪几个?...
  7. jcr多久更新一次_科学网—你知道吗,SCI影响因子数据在7月又有更新了 - LetPub编辑的博文...
  8. ModuleNotFoundError: No module named ‘models‘
  9. DeepFake技术--实际操作
  10. python基于内置的SimpleHTTPServer来搭建简易的FTP服务器实现局域网内文件共享