【泛型】Generic 参数化类型 类型转换
关于泛型的一些重要知识点
- 【? extends E】接收E类型或者E的子类型对象。上限。一般存储对象的时候用。比如 添加元素 addAll。
- 【? super E】接收E类型或者E的父类型对象。下限。一般取出对象的时候用。比如比较器。
- 1、表示多个可变类型之间的相互关系:HashMap<T,S>表示类型T与S的映射,HashMap<T, S extends T>表示T的子类与T的映射关系。
- 2、细化类的能力:ArrayList<T> 可以容纳任何指定类型T的数据,当T代指人,则是人的有序列表,当T代指杯子,则是杯子的有序列表,所有对象个体可以共用相同的操作行为。
- 3、复杂类型被细分成更多类型:List<People>和List<Cup>是两种不同的类型,这意味着List<People> listP = new ArrayList<Cup>()是不可编译的。这种检查基于编译时而非运行时,所以说是不可编译并非不可运行,因为运行时ArrayList不保留Cup信息。另外要注意,即使People继承自Object,List<Object> listO = new ArrayList<People>()也是不可编译的,应理解为两种不同类型。因为listO可以容纳任意类型,而实例化的People列表只能接收People实例,这会破坏数据类型完整性。
泛型的基本概念
public interface java.lang.reflect.ParameterizedType extends Type
public interface java.lang.reflect.GenericDeclaration
所有已知实现类:Class、Constructor、Method
实例分析
public class ArrayList{
public Object get(int i){......}
public void add(Object o){......}
......
private Object[] elementData;
}
- 没有错误检查,可以向数组列表中添加任何类的对象
- 在取元素的时候,需要进行强制类型转换
/**jdk1.5之前的写法,容易出问题*/
ArrayList arrayList1=new ArrayList();
arrayList1.add(1);
arrayList1.add(1L);
arrayList1.add("asa");
int i=(Integer) arrayList1.get(1);//因为不知道取出来的值的类型,类型转换的时候容易出错
/** jdk1.5之后加入泛型*/
ArrayList<String> arrayList2=new ArrayList<String>(); //限定数组列表中的类型
//arrayList2.add(1); //因为限定了类型,所以不能添加整形
//arrayList2.add(1L);//因为限定了类型,所以不能添加整长形
arrayList2.add("asa");//只能添加字符串
String str=arrayList2.get(0);//因为知道取出来的值的类型,所以不需要进行强制类型转换
泛型的使用
泛型类:类名后面
public class HashMap<K,V> {
public V put(K key, V value) {...}
public V get(Object key) {...}
...
}
public class Pair<T> {
private T value;
public Pair(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
public static void main(String[] args) throws ClassNotFoundException {
Pair<String> pair = new Pair<String>("Hello");//注意,"="号左边和右边都要使用<>指定泛型的实际类型
String str = pair.getValue();
pair.setValue("World");
}
class Pair<T, S, P, U, E> { }
泛型接口
interface Show<T,U>{
void show(T t,U u);
}
public class ShowTest implements Show<String, Date> {
@Override
public void show(String t, Date u) {
System.out.println(t + " " + u.getTime());
}
}
Show<String, Date> show = new ShowTest();
show.show("包青天", new Date());
泛型方法:返回值之前
class Person<S> {
public <W> void show(W w) {//这里的【W】完全等价于Object
if (w != null) System.out.println(w.toString());
}
public static <Y> void staticShow(Y y) {
if (y != null) System.out.println(y.toString());
//静态方法不能访问在类声明上定义的类型变量
//S s;//错误提示:Cannot make a static reference to the non-static type S
}
}
泛型变量的类型限定
- 无限定的泛型变量等价于Object(白哥添加)
- 不管该限定是类还是接口,统一都使用关键字 extends
- 可以使用 & 符号给出多个限定
- 如果限定既有接口也有类,那么类必须只有一个,并且放在首位置
public static <T extends Comparable> T get(T t1,T t2) //继承或实现都用extends
public static <T extends Comparable & Serializable> T get(T t1,T t2) //使用 & 符号给出多个限定
public static <T extends Object & Comparable & Serializable> T get(T t1,T t2) //继承的类Object必须放在首位
通配符?的使用
- 无限定通配符 形式<?>
- 上边界限定通配符 形式< ? extends Number>
- 下边界限定通配符 形式< ? super Number>
public static void main(String[] args) throws Exception {
List<Integer> listInteger = new ArrayList<Integer>();
printCollection(listInteger);//报错 The method printCollection(Collection<Object>) in the type Test is not applicable for the arguments (List<Integer>)
}
public static void printCollection(Collection<Object> collection) {
for (Object obj : collection) {
System.out.println(obj);
}
}
public static void printCollection(Collection<?> collection) {...}
collection.add(new Object());//The method add(capture#1-of ?) in the type Collection<capture#1-of ?> is not applicable for the arguments (Object)
List<? extends Number> x = new ArrayList<Integer>();//正确
List<? extends Number> y = new ArrayList<Object>();//错误 Type mismatch: cannot convert from ArrayList<Object> to List<? extends Number>
List<? super Number> y = new ArrayList<Object>();//正确
List<? super Number> x = new ArrayList<Integer>();//错误 Type mismatch: cannot convert from ArrayList<Integer> to List<? super Number>
类型擦除
ArrayList<String> list1 = new ArrayList<String>();
ArrayList<Integer> list2 = new ArrayList<Integer>();
System.out.println((list1.getClass() == list2.getClass()) + " " + (list1.getClass() == ArrayList.class));//true true
List<Integer> list = new ArrayList<Integer>();
list.add(10086);
Method method = list.getClass().getMethod("add", Object.class);
//运行时利用反射机制调用集合的add方法,跳过编译时的泛型检查
method.invoke(list, "虽然集合中对元素限定的泛型是Integer,但是也能通过反射把字符串添加到集合中");
Object object = list.get(1);
System.out.println(object.getClass().getSimpleName() + " " + (object.getClass() == String.class));//String true
try {
System.out.println(((Object) list.get(1)).getClass());//class java.lang.String
System.out.println(list.get(1).getClass());//如果不指定list.get(1)的类型,则会默认将其强制转换为集合上指定的泛型类型
} catch (Exception e) {
e.printStackTrace();//java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
}
类型擦除后保留的原始类型
class Pair<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
class Pair {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
public class Pair<T extends Comparable& Serializable> { ... }
public class Pair<T extends Serializable & Comparable>
- 在不指定泛型的时候,泛型变量的类型为 该方法中的几种类型的同一个父类的最小级,直到Object。
- 在指定泛型的时候,该方法中的几种类型必须是该泛型实例类型或者其子类。
public class Test {
public static void main(String[] args) {
/**不指定泛型的时候,泛型变量的类型为 该方法中的几种类型的同一个父类的最小级,直到Object*/
int i = Test.add(1, 2); //这两个参数都是Integer,所以T为Integer类型
Number f = Test.add(1, 1.2);//这两个参数一个是Integer,一个是Float,所以取同一父类的最小级,为Number
Object o = Test.add(1, "asd");//这两个参数一个是Integer,一个是Float,所以取同一父类的最小级,为Object
/**指定泛型的时候,该方法中的几种类型必须是该泛型实例类型或者其子类*/
int a = Test.<Integer> add(1, 2);//指定了Integer,所以只能为Integer类型或者其子类
//int b = Test.<Integer> add(1, 2.2);//编译错误,指定了Integer,不能为Float
Number c = Test.<Number> add(1, 2.2); //指定为Number,所以可以为Integer和Float
}
public static <T> T add(T x, T y) {
return y;
}
}
附加:GenericDeclaration 接口
public interface java.lang.reflect.GenericDeclaration
- TypeVariable<?>[] getTypeParameters() 返回声明顺序的 TypeVariable 对象的数组,这些对象表示由此 GenericDeclaration 对象表示的一般声明声明的类型变量。
- 返回:表示由此一般声明声明的类型变量的 TypeVariable 对象的数组
- 如果底层的一般声明未声明任何类型变量,则返回一个 0 长度的数组。
public static <T extends Person, U> void main(String[] args) throws Exception {
Method method = Test.class.getMethod("main", String[].class);
TypeVariable<?>[] tvs = method.getTypeParameters();//返回声明顺序的 TypeVariable 对象的数组
System.out.println("声明的类型变量有:" + Arrays.toString(tvs));//[T, U]
for (int i = 0; i < tvs.length; i++) {
GenericDeclaration gd = tvs[i].getGenericDeclaration();
System.out.println("【GenericDeclaration】" + gd);//public static void com.bqt.Test.main(java.lang.String[]) throws java.lang.Exception
System.out.println(gd.getTypeParameters()[i] == tvs[i]);//true。 GenericDeclaration和TypeVariable两者相互持有对方的引用
System.out.println(tvs[i] + " " + tvs[i].getName() + " " + Arrays.toString(tvs[i].getBounds()));//T T [class com.bqt.Person] 和 U U [class java.lang.Object]
}
}
【泛型】Generic 参数化类型 类型转换的更多相关文章
- 泛型 Generic 类型擦除引起的问题及解决方法
参考:http://blog.csdn.net/lonelyroamer/article/details/7868820#comments 因为种种原因,Java不能实现真正的泛型,只能使用类型擦除来 ...
- Java之集合初探(二)Iterator(迭代器),collections,打包/解包(装箱拆箱),泛型(Generic),comparable接口
Iterator(迭代器) 所有实现了Collection接口的容器都有一个iterator方法, 用来返回一个实现了Iterator接口的对象 Iterator对象称作迭代器, 用来方便的实现对容器 ...
- C#泛型(Generic)
一.什么是泛型 泛型(Generic)是C#语言2.0.通用语言运行时(CLR)2.0..NET Framework2.0推出来的新特性. 泛型为.NET框架引入类型参数(Type Parameter ...
- Java - 泛型 ( Generic )
Java - 泛型 ( Generic ) > 泛型的特点 > 解决元素存储的安全性问题 > 解决获取数据元素时,需要类型强转的问题 ...
- 谈一谈从 Delphi 2009 之后就支援的重要功能 – 泛型 (Generic)
前言 在C++的语言基础当中,除了物件导向.事件驱动的概念之外,模版设计(Template)也是非常重要的一环.然而,C++的开发人员能够善用模版设计的并不多.模版设计这个好物,一般还有一个名称,就是 ...
- JAVA中的泛型(Generic)
Java泛型(Generic)简介 泛型是jdk1.5版本以后推出来的,表示类型参数化,让java能更具有动态性一些,让类型能变成参数传递. 要我自己感觉的话,泛型本身没啥用,跟反射在一起用,就体现出 ...
- Dephi泛型generic的应用
Dephi泛型generic的应用 泛型在C++, C#中已有广泛应用,Delphi自2009版本也引入泛型,典型的应用如TList,TDictionary.如果你熟悉C#,其用法十分类似. 比如 ...
- Java基础之Comparable接口, Collections类,Iterator接口,泛型(Generic)
一.Comparable接口, Collections类 List的常用算法: sort(List); 排序,如果需要对自定义的类进行排序, 那就必须要让其实现Comparable接口, 实现比较两个 ...
- Java自学-集合框架 泛型Generic
ArrayList上使用泛型 步骤 1 : 泛型 Generic 不指定泛型的容器,可以存放任何类型的元素 指定了泛型的容器,只能存放指定类型的元素以及其子类 package property; pu ...
随机推荐
- Laravel框架初学一路由(基本路由)
基本路由 Laravel最基本的路由:接收一个URI和Closure闭包函数 ,提供了定义路由的一种非常简单且富有表达力的方式 Route::get("foo", function ...
- MySQL Insert语句单个批次数量过多导致的CPU性能问题分析
[问题] 最近有台服务器比较频繁的CPU报警,表现的特征有CPU sys占比偏高,大量慢查询,大量并发线程堆积.后面开发对insert的相关业务限流后,服务器性能恢复正常. [异常期间线程处理情况] ...
- OSI、TCP、IP、UDP 这些都是啥??
一个大大的问号首先抛出,计算机之间是如何进行通信的? 计算机网络是通过传输介质.通信设施和网络通信协议,把分散在不同地点的计算机设备互连起来,实现资源共享和数据传输的系统. 网络协议就是数据按照一定的 ...
- odoo基础数据加载
odoo 基础数据加载 这里介绍的odoo基础数据加载分两种方式,一种是演示数据加载,一种是默认数据加载,下面就是详细介绍 首先,当然是创建一个date文件夹 项目目录,右键自定义一个文件夹 XML数 ...
- OpenStack计费项目Cloudkitty系列详解(一)
云计算是一种按需付费的服务模式,虽然OpenStack前期在计量方面走了些“弯路”,但现在的ceilometer.gnocchi.aodh.panko项目的稳步并进算是让其峰回路转.然而,目前来看Op ...
- Urllib库的基本用法
1.什么是url? 统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址. 基本URL包含模式(或称协议).服务器名称(或IP地址).路径和文件名,如“ ...
- BZOJ 4289: PA2012 Tax 差分建图 最短路
https://www.lydsy.com/JudgeOnline/problem.php?id=4289 https://www.cnblogs.com/clrs97/p/5046933.html ...
- Illegal instruction错误的定位---忽略编译期警告的代价
在原计算机的linux c++程序可以正确跑起来,但是换了一台机器运行时出现致命错误,程序直接当掉,错误提示如下: Illegal instruction (core dumped) 造成改错的主要原 ...
- js解析顺序了解一下??
我们在学习一种新事物的时候,总是知其然,而不知其所然.有些人会探究到底,有一些人会得过且过. 好了,开场白结束,直接进入正题. js不像C语言那样只要编译一次之后成.exe文件之后就不用在编译可以直接 ...
- adb root : adbd cannot run as root in production builds
在有些android手机上使用adb root希望获取root权限时出现如下提示信息:adbd cannot run as root in production builds.此时提升root权限的方 ...