Java泛型(T)与通配符?
前言:使用泛型的目的是利用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)与通配符?的更多相关文章
- Java泛型中的通配符
Java泛型中的通配符可以直接定义泛型类型的参数.而不用把该函数定义成泛型函数. public class GenericsTest { public static void main(String[ ...
- 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 下界通 ...
- Java泛型二:通配符的使用
原文地址http://blog.csdn.net/lonelyroamer/article/details/7927212 通配符有三种: 1.无限定通配符 形式<?> 2.上边界限定 ...
- JAVA 泛型中的通配符 T,E,K,V,?
前言 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据 ...
- 【转】聊一聊-JAVA 泛型中的通配符 T,E,K,V,?
原文:https://juejin.im/post/5d5789d26fb9a06ad0056bd9 前言 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型 ...
- Java 泛型中的通配符
本文内容如下: 1. 什么是类型擦除 2.常用的 ?, T, E, K, V, N的含义 3.上界通配符 < ?extends E> 4.下界通配符 < ?super E> 5 ...
- 关于JAVA泛型中的通配符类型
之前对JAVA一知半解时就拿起weiss的数据结构开始看,大部分数据结构实现都是采取通配符的思想,好处不言而喻. 首先建立两个类employee和manager,继承关系如下.其次Pair类是一个简单 ...
- Java泛型中的通配符的使用
package com.srie.testjava; import java.util.ArrayList; import java.util.List; public class TestClass ...
- Java泛型 通配符? extends与super
Java 泛型 关键字说明 ? 通配符类型 <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类 <? super T> 表示类型下界(Java ...
随机推荐
- python 函数(实参与形参、传递参数)
函数 什么是函数?函数是带名字的代码块,用于完成具体的工作.写出一个函数后,就可以一直调用. 定义函数,函数的基本组成: 1.1 向函数传递参数 向函数中传递任意参数,这样打印出的结果就可以根据自己的 ...
- 2018icpc徐州网络赛-H Ryuji doesn't want to study(线段树)
题意: 有n个数的一个数组a,有两个操作: 1 l r:查询区间[l,r]内$a[l]*(r-l+1)+a[l+1]*(r-l)+a[l+2]*(r-l-1)+\cdots+a[r-1]*2+a[r] ...
- java 利用POI 读取Execel数据的真实行数
java 利用poi 读execel文件的操作,读取总的数据行数一般是通过调用 sheet.getLastRowNum() ;可是这样有时候会出现一些问题,例如,当其中一行的数据的确都为空,可是其原本 ...
- 批处理(BAT) Ping监控, 结果记录入日志文件
::执行效果 @echo off ::等待用户输入需要监控IP set /p ip=Input the IP required to monitor: echo executing...... :st ...
- 开发中常见的common.js--1
common.js 1.基于代码重用的目的,方便在页面中调用: 2.跟普通的js包含文件并没有其他不一样,主要是一些js经常用到的函数,或者字符串.数组方法的简单扩展: 3.封装的兼容性的方法.[po ...
- [Effective Java 读书笔记] 第二章 创建和销毁对象 第三 四条
第三条 用私有构造器或者枚举类型强化singleton属性 singleton指只能被实例化一次的类,即将构造器设置为私有,使用公有静态成员来实例化,且只实例化一次对象 第四条 通过私有构造器强化不可 ...
- Asp.Net Api+Swagger控制器注释
Swagger注释不显示,只需要进入Startup.cs 找到: c.IncludeXmlComments(Path.Combine(AppDomain.CurrentDomain.BaseDirec ...
- MacBook通过SSH远程访问Parallel中的Ubuntu简明教程
作为一个前端,后端也需要了解,最终选择PHP入手学习,本来想选择Python,思前想后还是PHP作为Web开发比较合适,环境最终选择Ubuntu开发,由于是第一次,遇到不少坑,经过不懈的努力不断Goo ...
- centos7安装node.js
安装版本:node-v10.15.3 一.安装必要的编译软件包 # yum install gcc gcc-c++ -y 二.从源码下载Nodejs 进入官网选择自己需要的版本 https://nod ...
- form中label标签对齐,内容右对齐
给label设置一个固定长度即可: label{ display:inline-block; width:100px; text-align:right; }