前言:使用泛型的目的是利用Java编译机制,在编译过程中帮我们检测代码中不规范的有可能导致程序错误的代码。例如,我们都知道list容器可以持有任何类型的数据,所以我们可以把String类型和Integer等等同时可以放入同一个list容器中,但这种做法是极其危险的。在泛型机制中这种操作是编译不通过,会强制你修改。故帮我们减少了隐藏的bug.

一:泛型  T

1.1  泛型用法

根据泛型使用的位置,即用在类(class),属性(filed)和方法(method)的不同位置,我把它分别总结如下几种

  泛型类:即在类名后添加泛型标识(<T ....>),表示该class持有的一种类型。

  泛型属性:泛型属性必须结合泛型类使用,用于接受泛型类持有的类型T

  泛型方法:即在方法的返回值前声明泛型<T extends ***>,该泛型T是对该方法的参数T的一种限定。

  备注1:如果泛型T没有被extends修饰(包含类和方法),我们称之为无界泛型,如果被extends修饰我们称之为有界泛型如下。

  备注2:如果方法参数中有泛型T,而方法的返回类型前没有泛型T,该类型不是泛型方法,而是泛型类。

  备注3:泛型方法常用在工具类中(即该方法只是一种工具),即与类的实例对象关系(持有的方法无关)。

  备注4:当泛型方法中的泛型T与类中的泛型T同名时会产生警报,因为编译器不确定你要使用那个(方法中一个,类中也一个)持有对象。

1.2  有界泛型

  相较于无界泛型(没有限定类型)<T>的用法,我们可以使用有界泛型<T extends ****>来限定类持有对象的范围,或泛型方法传入该方法参数的范围。以保证业务逻辑的正确执行。

备注1:有界泛型只有上界(extends),没有下界的用法(相比于通配符?)。

1.3  泛型继承

一行代码加两幅图带你体会它与我们接口,抽象类和类的区别

ArrayList<String> arrayList = new ArrayList<>();
Object object = new Object();
//The method add(String) in the type ArrayList<String> is not applicable for the arguments (Object)
arrayList.add(object);//因为 ArrayList<String>不是 ArrayList<Object>的子类 。

  

二:通配符?

这是一段java官方对通配符的定义,In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific). The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype.从这里我们可以看出通配符?是一种未知的类型。

个人小结:常用在方法上(注意与泛型方法的区别,其不需要再方法的返回类型前声明)

2.1 上界通配

  即定义通配符的上界,用关键字extends声明,例如

public static void process(List <?extends Foo> list){/ * ... * /}

2.2 无界通配

  即不限制通配符的界限,不需要任何关键字修饰‘?’,例如

public static void printList(List <?> list){/*........*/}

说明其功能形式 public static void printList(List <Object> list){/*........*/},但还是有区别的。

  注意:List <Object>List <?>是不一样的。List <Object> 可以插入Object,或任何Object对象的子类,成列表<对象>。但是你只能在List <?>中插入null()

public class TestWildcard {
public void printList(List<String> list) {
for (Object elem: list)
System.out.print(elem + " ");
System.out.println();
}
public void printList2(List<?> list) {
for (Object elem: list)
System.out.print(elem + " ");
System.out.println();
}
public static void main(String[] args) {
TestWildcard testWildcard = new TestWildcard();
ArrayList<? extends Object> arrayList = new ArrayList<>();
ArrayList<Object> arrayList2 = new ArrayList<>();
arrayList.add(null);
//arrayList.add(testWildcard);
arrayList2.add(null);
arrayList2.add("2"); List<Integer> li = Arrays.asList(1, 2, 3);
List<String> ls = Arrays.asList("one", "two", "three");
testWildcard.printList2(li);
testWildcard.printList2(ls);
//testWildcard.printList(li);报错
testWildcard.printList(ls); }
} 

2.3 下届通配

即定义通配符的下super界,用关键字extends声明,例如

public static void addNumbers(List <?super Integer> list){}

2.4 通配子类

                                 

The common parent is List<?>.                                                               A hierarchy of several generic List class declarations.

2.5 通配捕获与辅助方法

通配捕获:即操作通配符?参数  会抛出异常,除null外,例如:

public class WildcardError {
void foo(List<?> i) {
i.set(0, i.get(0));
}
}

分析:根据编译器推断,?是一个Object类型(故可以遍历出?所代表的对象),但如果要操作List<?>对象,编译器会要求?代表的具体类型,而编译器通过现有的规则(真对 ?的规则)是不允许的,故会包错。

解决捕获辅助方法

public class WildcardFixed {
void foo(List<?> i) {
fooHelper(i);
}
// Helper method created so that the wildcard can be captured
// through type inference.
private <T> void fooHelper(List<T> l) {
l.set(0, l.get(0));
}
}

  通过一个辅助方法  ***  fooHelper(List<T> l){}就解决了

分析:根据规则(真对T),编译器就知道T的具体类型,故可以安全的操作该对象。

三:小结

泛型与通配符区别:最根本的区别就是,java编译器,把T(泛型)推断成具体类型,而把通配符?推断成未知类型。而java编辑器只能操作具体类型,不能操作未知类型。导致如果有对参数有修改的操作就必须要使用泛型,如果仅是查看就可以使用通配符.

利用以上推断,我们可以利用通配符特性设计出安全的接口,比如我在一个接口的方法定义了通配符参数,则继承该接口的所有方法,都不能修改该方法传递过来的参数。

例如:

public interface GInterface {

	<T> void foo(List<? extends T> list);
} public class GIterfaceImpl implements GInterface{ @Override
public <T> void foo(List<? extends T> list) {
/**
* 只能遍历list,不能修改list
*/
for (T t : list) {
System.out.println(t);
}
//list.add(new Object());
} public static void main(String[] args) {
GIterfaceImpl gIterfaceImpl = new GIterfaceImpl();
ArrayList<String> list = new ArrayList<>();
list.add("1");
gIterfaceImpl.foo(list); }
}

  

四:延伸

泛型与java8:

参考资料:https://docs.oracle.com/javase/tutorial/java/generics/index.html

Java泛型(T)与通配符?的更多相关文章

  1. Java泛型中的通配符

    Java泛型中的通配符可以直接定义泛型类型的参数.而不用把该函数定义成泛型函数. public class GenericsTest { public static void main(String[ ...

  2. Java泛型中的通配符T,E,K,V

    Java泛型中的通配符T,E,K,V 1.泛型的好处 2.泛型中的通配符 2.1 T,E,K,V,? 2.2 ?无界通配符 2.3 上界通配符 < ? extends E> 2.4 下界通 ...

  3. Java泛型二:通配符的使用

    原文地址http://blog.csdn.net/lonelyroamer/article/details/7927212 通配符有三种: 1.无限定通配符   形式<?> 2.上边界限定 ...

  4. JAVA 泛型中的通配符 T,E,K,V,?

    前言 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据 ...

  5. 【转】聊一聊-JAVA 泛型中的通配符 T,E,K,V,?

    原文:https://juejin.im/post/5d5789d26fb9a06ad0056bd9 前言 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型 ...

  6. Java 泛型中的通配符

    本文内容如下: 1. 什么是类型擦除 2.常用的 ?, T, E, K, V, N的含义 3.上界通配符 < ?extends E> 4.下界通配符 < ?super E> 5 ...

  7. 关于JAVA泛型中的通配符类型

    之前对JAVA一知半解时就拿起weiss的数据结构开始看,大部分数据结构实现都是采取通配符的思想,好处不言而喻. 首先建立两个类employee和manager,继承关系如下.其次Pair类是一个简单 ...

  8. Java泛型中的通配符的使用

    package com.srie.testjava; import java.util.ArrayList; import java.util.List; public class TestClass ...

  9. Java泛型 通配符? extends与super

    Java 泛型 关键字说明 ? 通配符类型 <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类 <? super T> 表示类型下界(Java ...

随机推荐

  1. (六)mybatis拦截器

    首先拦截器可以拦截mybatis四大核心对象:StatementHandler,ParameterHandler,ResultSetHandler,Executor,具体拦截时根据需求拦截一部分方法 ...

  2. C++ STL——优先队列的结构体表示方法

    优先队列是队列的一种,但是自身具有一定的排序功能,所以不具有队列“先进先出”的性质 刚刚接触优先队列,看过网上的用法后感觉还是太过于朦胧,所以打算自己写一个稍微细节一点的. 头文件 #include& ...

  3. 从底层入手,解析字节码增强和Btrace应用

    这篇文章聊下字节码和相关的应用. 1.机器码和字节码 机器码(machine code),学名机器语言指令,有时也被称为原生码(Native Code),是电脑的CPU可直接解读的数据. 通常意义上来 ...

  4. ELF文件之三——使用链接脚本-2个函数

    main.c int main() { ; } int add() { ; } main.o的比较 与之二相比,section header offset由0x90变为0xA4,增加0x14,即add ...

  5. 详解CopyOnWrite容器及其源码

    详解CopyOnWrite容器及其源码 在jave.util.concurrent包下有这样两个类:CopyOnWriteArrayList和CopyOnWriteArraySet.其中利用到了Cop ...

  6. Linux Firewalld用法及案例

    Firewalld概述 动态防火墙管理工具 定义区域与接口安全等级 运行时和永久配置项分离 两层结构 核心层 处理配置和后端,如iptables.ip6tables.ebtables.ipset和模块 ...

  7. 再次小结windows服务的编写

    2013-03-23 21:05 (分类:计算机程序) 其实很简单 void mian() { //服务的分派表    SERVICE_TABLE_ENTRY DispatchTable[] ={   ...

  8. apache 负载均衡

    此次使用mod_proxy的方式来实现的,因为在Apache2以上的版本中已经集成了,因此不需要再另行安装和配置了. 只需要把注释去掉即可,去掉以下模块的注释: LoadModule proxy_mo ...

  9. 智和网管平台SugarNMS 2019年度IT综合监控突破性成果概览

    一元复始,万象更新,欢辞旧岁,喜迎新年. 智和信通,精益求精,携手并进,迎战鼠年! 2020年1月10日,北京智和信通技术有限公司(以下简称“智和信通”)以“2020携手并进”为主题的年度庆典暨201 ...

  10. C语言四

    c语言循环结构 为什么用循环? C语言中的循环主要分为三种:while循环,do...while循环 for循环 while循环:先判断条件是否成立,如果条件成立将执行其下的循环体,如果条件不成立一次 ...