前言:使用泛型的目的是利用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. qt creator源码全方面分析(2-6)

    目录 User Interface Text Guidelines 语法和风格 标点 编写工具提示tooltips 编写消息 UI文本大写 使用书本样式大写 使用句子样式大写 准备本地化 标记UI文本 ...

  2. HDU 1004 Let the Balloon Rise(STL初体验之map)

    Problem Description Contest time again! How excited it is to see balloons floating around. But to te ...

  3. num13---外观模式/过程模式

    假设家庭影院有一系列设备,每个设备都有各种开闭等功能性方法,使用家庭影院功能的时候,需要进行各个设备的一系列操作,繁琐麻烦. 现在提供一个外观类,在里面定义操作流程,客户端只需要和外观类进行接口交互即 ...

  4. JMeter接口测试-接口签名校验

    前言 很多HTTP接口在传参时,需要先对接口的参数进行数据签名加密 如pinter项目的中的签名接口 http://localhost:8080/pinter/com/userInfo 参数为: {& ...

  5. Django设置异步任务

    1.安装Django-celery 包:pip install django-celery==3.2.2 2.开启redis服务 需要使用redis做broker,所以在使用异步和定时任务时需要开启r ...

  6. pocsuite3使用教程

    pocsuite3使用教程 0X00简介 PocSuite3是Knownsec 404安全研究团队设计的一款远程漏洞测试以及PoC开发框架,该框架使用了功能极其强大的概念验证引擎,并自带了大量渗透测试 ...

  7. 08.JS单词整理

    以下为按照文章顺序简单整理的JS单词, 注意:是JS单词注释,部分与英文不符 01.JS语法规范.变量与常量 console——控制台 log——日志 var——变量 variable变量,变化 co ...

  8. StarUML之四、StarUML的Diagrams(图)与Elements(元素)及相关属性

    Diagrams(图)可以理解为画布  1:创建图 在右侧的Model Explorer管理界面的第一个节点右键,或者选择菜单中Model | Add Diagram | [DiagramType]都 ...

  9. 盒模型大小取决于它的padding,margin,border数值

    盒模型规定了元素框处理元素内容width与height值.内边距padding.边框border 和 外边距margin 的数值大小.边框内的空白是内边距padding,边框外的空白是外边距margi ...

  10. 11.Android-Xml读写

    android中写XML时,需要用到XmlSerializer类 解析XML时,则需要用到XmlPullParser类 1.XmlSerializer类介绍 通过Xml.newSerializer() ...