Java泛型的用法及T.class的获取过程解析

这篇文章主要介绍了Java泛型的用法及T.class的获取过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

胡乱总结泛型的四点作用:

第一是泛化,可以拿个T代表任意类型。 但GP是被C++严苛的静态性逼出来的,落到Java、C#这样的花语平原里----所有对象除几个原始类型外都派生于Object,再加上Java的反射功能,Java的Collection库没有范型一样过得好好的。

第二是泛型 + 反射,原本因为Java的泛型拿不到T.class而觉得泛型没用,最近才刚刚学到通过反射的API来获取T的Class,后述。

第三是收敛,就是增加了类型安全,减少了强制类型转换的代码。这点倒是Java Collection历来的弱项。

第四是可以在编译期搞很多东西,比如MetaProgramming。但除非能完全封闭于框架内部,框架的使用者和扩展者都不用学习这些东西的用法,否则那就是自绝于人民的票房毒药。C++的MetaProgramming好厉害吧,但对比一下Python拿Meta Programming生造一个Class出来的简便语法,就明白什么才是真正的叫好又叫座。

所以,作为一个架构设计师,应该使用上述的第2,3项用法,在框架类里配合使用反射和泛型,使得框架的能力更强; 同时采用收敛特性,本着对人民负责的精神,用泛型使框架更加类型安全,更少强制类型转换。

擦拭法避免了Java的流血分裂 :

大家经常骂Java GP的擦拭法实现,但我觉得多亏于它的中庸特性---如果你用就是范型,不用就是普通Object,避免了Java阵营又要经历一场to be or not to be的分裂。
最大的例子莫过Java 5的Collection 框架, 比如有些同学坚持认为自己不会白痴到类型出错,而且难以忍受每个定义的地方都要带一个泛型定义List〈Book〉,不用强制类型转换所省下的代码还不够N处定义花的(对了,java里面还没有tyepdef.....),因此对范型十分不感冒,这时就要齐齐感谢这个搽拭法让你依然可以对一个泛型框架保持非泛型的用法了...

<<干货来了!!!!!!>>

通过反射获得 T.class:

abstract public class BaseHibernateEntityDao<T> extends HibernateDaoSupport {
    private Class<T> entityClass;
    public BaseHibernateEntityDao() {
      entityClass =(Class<T>) ((ParameterizedType) getClass()
          .getGenericSuperclass()).getActualTypeArguments()[0];
} public T get(Serializable id) { T o = (T) getHibernateTemplate().get(entityClass, id); return o; } }

重点就是这句话:

Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; 

解释:

1. public Type getGenericSuperclass()

用来返回表示当前Class 所表示的实体(类、接口、基本类型或 void)的直接超类的Type。如果这个直接超类是参数化类型的,则返回的Type对象必须明确反映在源代码中声明时使用的类型。比如:

import java.lang.reflect.ParameterizedType;
public class GT1 extends GT2<Integer>{
  public static void main(String[] args) {
    System.out.println(((ParameterizedType)new GT1().getClass().getGenericSuperclass()));
  }
}

则输出结果即为:

GT2<java.lang.Integer>

如果此Class代表的是Object 类、接口、基本类型或 void,则返回 null。。如果此对象表示一个数组类,则返回表示 Object 类的 Class 对象。

2. public Type[] getGenericInterfaces()

与上面那个方法类似,只不过Java的类可以实现多个接口,所以返回的Type必须用数组来存储。

以上两个方法返回的都是Type对象或数组,在我们的这个话题中,Class都是代表的参数化类型,因此可以将Type对象Cast成ParameterizedType对象。而 ParameterizedType对象有一个方法, getActualTypeArguments()。

public Type[] getActualTypeArguments()

用来返回一个Type对象数组,这个数组代表着这个Type声明中实际使用的类型。接着使用上面的例子:

import java.lang.reflect.ParameterizedType;
public class GT1 extends GT2<Integer>{
  public static void main(String[] args) {
  System.out.println(((ParameterizedType)new GT1().getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
  }
}

这次的显示结果将是:class java.lang.Integer

因此,我们可以通过继承+反射的方法,来的到T.class。

需要说明的是,江南白衣使用的方法是将关键语句

 Class < T > entityClass = (Class < T > ) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[ 0 ];

放在了超类,也就是声明泛型的那个类的构造方法中。这样一来,子类在继承具有泛型的超类时,会自动调用超类的构造方法。在此超类的构造方法中,调用的getClass返回的是子类的Class类型(与通常的重写机制有悖,呵呵,有待深究,但测试结果确是如此),则在子类中就无需再显式地使用getGenericInterfaces()和getGenericSuperclass()等方法了。

接着,再使用(Class<T>)对 getActualTypeArguments()返回的元素做casting,即可得到所谓的T.class。

泛型之后,所有BaseHibernateEntityDao的子类只要定义了泛型,就无需再重载getEnttityClass(),get()函数和find()函数,销益挺明显的,所以SpringSide的Dao基类毫不犹豫就泛型了。

不过擦拭法的大棒仍在,所以子类的泛型语法可不能乱写,最正确的用法只有:

public class BookDao extends BaseHibernateEntityDao<Book>

个人见解:即他们之间的关系就是 BaseHibernateEntityDao 是一个父类,他可以是单纯的一个类,也可以是实现接口的类,而应其中泛型的缘故,所以他直接实例化调用其中方法是没有意义的,因为泛型的类型无法确定,所以只有通过用其它类去继承它,在继承时将泛型传入,此时若实例化继承了BaseHibernateEntityDao 的类,BaseHibernate EntityDao的构造器中就会通过反射确定该泛型的类型.(有误请指正)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java 使用getClass().getResourceAsStream()方法获取资源

    Java 使用getClass().getResourceAsStream()方法获取资源 之前想获取一个资源文件做一些处理,使用getClass().getResourceAsStream()一直拿不到文件. 具体的用法. 1 InputStream is = this.getClass().getResourceAsStream(fileName); //拿不到资源 2 InputStream is = this.getClass().getResourceAsStream("/"

  • java中staticclass静态类详解

    一般情况下是不可以用static修饰类的.如果一定要用static修饰类的话,通常static修饰的是匿名内部类. 在一个类中创建另外一个类,叫做成员内部类.这个成员内部类可以静态的(利用static关键字修饰),也可以是非静态的.由于静态的内部类在定义.使用的时候会有种种的限制.所以在实际工作中用到的并不多. 在开发过程中,内部类中使用的最多的还是非静态地成员内部类.不过在特定的情况下,静态内部类也能够发挥其独特的作用. 一.静态内部类的使用目的. 在定义内部类的时候,可以在其前面加上一个权限

  • java动态添加外部jar包到classpath的实例详解

    java动态添加外部jar包到classpath的实例详解 前言: 在项目开发过程中我们有时候需要动态的添加外部jar包,但是具体的业务需求还没有遇到过,因为如果动态添加外部jar包后,我们就需要修改业务代码,而修改代码就需要重新启动服务,那样好像就没有必要动态添加外部jar包了,怎么样才能不重新启动服务器就可以使用最新的代码我没有找到方法,如果各位知道的话给我点建议,回归主题,实现动态添加外部jar包到classpath的方法如下: String beanClassName = "com.dy

  • 浅谈java中类名.class, class.forName(), getClass()的区别

    Class对象的生成方式如下: 1.类名.class    说明: JVM将使用类装载器, 将类装入内存(前提是:类还没有装入内存),不做类的初始化工作.返回Class的对象 2.Class.forName("类名字符串")  (注:类名字符串是包名+类名) 说明:装入类,并做类的静态初始化,返回Class的对象 3.实例对象.getClass()  说明:对类进行静态初始化.非静态初始化:返回引用o运行时真正所指的对象(因为:子对象的引用可能会赋给父对象的引用变量中)所属的类的Cla

  • Java 获取泛型的类型实例详解

    Java 获取泛型的类型实例详解 Java 泛型实际上有很多缺陷,比如不能直接获取泛型的类型,不能获取带泛型类等. 以下方式是不正确的: ①.获取带泛型的类的类型 Class lstUClazz = List<User>.class ②获取局部变量泛型的类型 List<User> listUser = new ArrayList<User>(); Type genType = listUser.getClass().getClass().getGenericSuperc

  • 详解Java中-classpath和路径的使用

    javac -classpath的使用: javac:如果当前你要编译的Java文件中引用了其它的类(比如说:继承),但该引用类的.class文件不在当前目录下,这种情况下就需要在javac命令后面加上-classpath参数,通过使用以下三种类型的方法 来指导编译器在编译的时候去指定的路径下查找引用类. (1).绝对路径:javac -classpath c:/junit3.8.1/junit.jar   Xxx.java (2).相对路径:javac -classpath ../junit3

  • IntelliJ IDEA引入第三方jar包或查看Java源码的时候报decompiled.class file bytecode version:52.0(java 8)错误的解决办法

    引入第三方jar包或者查看Java源码的时候,经常遇到问题如下: decompiled.class file bytecode version:52.0(java 8) ,open source file. 翻译一下上面的意思:(反编译 .class文件中的字节码 到Java 8 版本) 打开源文件 其实这个时候,你就得注意一下,你看到文件是 .class文件,还是 .java文件. 然后我就详细的总结了一下,二者的差别: 1.先是理论知识: .java为Java的源文件后缀,编写的代码需要写在

  • Java泛型的用法及T.class的获取过程解析

    这篇文章主要介绍了Java泛型的用法及T.class的获取过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 胡乱总结泛型的四点作用: 第一是泛化,可以拿个T代表任意类型. 但GP是被C++严苛的静态性逼出来的,落到Java.C#这样的花语平原里----所有对象除几个原始类型外都派生于Object,再加上Java的反射功能,Java的Collection库没有范型一样过得好好的. 第二是泛型 + 反射,原本因为Java的泛型拿不到T.cla

  • JAVA泛型的继承和实现、擦除原理解析

    这篇文章主要介绍了JAVA泛型的继承和实现.擦除原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 很多的基础类设计会采用泛型模式,有些应用在使用的时候处于隔离考虑,会进行继承,此时子类如何继承泛型类就很讲究了,有些情况下需要类型擦除,有些情况下不需要类型擦除,但是大多数情况下,我们需要的是保留父类的泛型特性.因为类型被擦除后,得到的对象会是Object,此时会导致编码或反序列化失败. 如下所示: 如果子类也继承父类的泛型化,则类型不会被擦

  • Java中泛型的用法总结

    本文实例总结了Java中泛型的用法.分享给大家供大家参考.具体如下: 1 基本使用 public interface List<E> { void add(E); Iterator<E> iterator(); } 2 泛型与子类 Child是Parent的子类,List<Child>却不是List<Parent>的子类. 因此:List<Object> list = new ArrayList<String>()是错误的. 如果上面

  • Java 泛型总结(一):基本用法与类型擦除

    简介 Java 在 1.5 引入了泛型机制,泛型本质是参数化类型,也就是说变量的类型是一个参数,在使用时再指定为具体类型.泛型可以用于类.接口.方法,通过使用泛型可以使代码更简单.安全.然而 Java 中的泛型使用了类型擦除,所以只是伪泛型.这篇文章对泛型的使用以及存在的问题做个总结,主要参考自 <Java 编程思想>. 这个系列的另外两篇文章: Java 泛型总结(二):泛型与数组 Java 泛型总结(三):通配符的使用 基本用法 泛型类 如果有一个类 Holder 用于包装一个变量,这个变

  • Java泛型定义与用法实例详解

    本文实例讲述了Java泛型定义与用法.分享给大家供大家参考,具体如下: 1. 泛型的由来 先看如下代码: import java.util.List; import java.util.ArrayList; public class TestGeneric { @SuppressWarnings({ "rawtypes", "unchecked" }) public static void main(String[] args) { List list = new

  • Java泛型继承原理与用法详解

    本文实例讲述了Java泛型继承原理与用法.分享给大家供大家参考,具体如下: 一 点睛 当创建了带泛型声明的接口.父类之后,可以为该接口创建实现类,或从该父类来派生子类,但值得指出的是,当使用这些接口.父类时不能再包含类型形参. 如果使用泛型类时没有传入实际的类型参数,Java编译器可能发出警告:使用了未经检查或不安全的操作--这就是泛型检查的警告. 二 实战--传入实际的类型参数 public class A1 extends Apple<String> { // 正确重写了父类的方法,返回值

  • Java泛型定义与用法入门示例

    本文实例讲述了Java泛型定义与用法.分享给大家供大家参考,具体如下: 一 引入泛型背景 Java集合不会知道它们需要用它来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要求具有很好的通用性.但这样做也带来两个问题: 集合对元素类型没有任何限制,这样可能引发一些问题:例如想创建一个只能保存Dog对象的集合,但程序也可以轻易地将Cat对象"丢"进去,所以可能引发异常. 由于把对象"丢进"集合时,集合丢失了对象的状态信息,集合只知道它盛装的是Objec

  • Java泛型映射不同的值类型详解及实例代码

    Java泛型映射不同的值类型详解 前言: 一般来说,开发人员偶尔会遇到这样的情形: 在一个特定容器中映射任意类型的值.然而Java 集合API只提供了参数化的容器.这限制了类型安全地使用HashMap,如单一的值类型.但如果想混合苹果和梨,该怎样做呢? 幸运的是,有一个简单的设计模式允许使用Java泛型映射不同的值类型,Joshua Bloch在其<Effective Java>(第二版,第29项)中将其描述为类型安全的异构容器(typesafe hetereogeneous Containe

  • Java 泛型总结(三):通配符的使用

    简介 前两篇文章介绍了泛型的基本用法.类型擦除以及泛型数组.在泛型的使用中,还有个重要的东西叫通配符,本文介绍通配符的使用. 这个系列的另外两篇文章: Java 泛型总结(一):基本用法与类型擦除 Java 泛型总结(二):泛型与数组 数组的协变 在了解通配符之前,先来了解一下数组.Java 中的数组是协变的,什么意思?看下面的例子: class Fruit {} class Apple extends Fruit {} class Jonathan extends Apple {} class

  • Java 泛型总结(二):泛型与数组

    简介 上一篇文章介绍了泛型的基本用法以及类型擦除的问题,现在来看看泛型和数组的关系.数组相比于Java 类库中的容器类是比较特殊的,主要体现在三个方面: 数组创建后大小便固定,但效率更高 数组能追踪它内部保存的元素的具体类型,插入的元素类型会在编译期得到检查 数组可以持有原始类型 ( int,float等 ),不过有了自动装箱,容器类看上去也能持有原始类型了 那么当数组遇到泛型会怎样? 能否创建泛型数组呢?这是这篇文章的主要内容. 这个系列的另外两篇文章: Java 泛型总结(一):基本用法与类

随机推荐