`
zcy860511
  • 浏览: 20539 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

Java获得泛型类型

阅读更多
最近要想获得泛型类型,总结多方意见,再通过实践,最终获得了结果。
当然也被许多文章给误导过……
下面我们看一个例子,这个例子是我自己写的
/*
 * Copyright 2010 Sandy Zhang
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * 
 */
package org.javazone.jroi.test.reflect;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Sandy Zhang
 */
public class Bean
{
	public Map<String, ListBean> list = new HashMap<String, ListBean>();

	public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException
	{
		Field c = Bean.class.getField("list");
		Field f = Field.class.getDeclaredField("signature");
		f.setAccessible(true);
		System.out.println(((String) f.get(c)));

	}
}

这个只是我最近在写一个反射调用的东西想到的问题,所以很无奈才必须要这样得到东西
下面是结果
Ljava/util/Map<Ljava/lang/String;Lorg/javazone/jroi/test/reflect/ListBean;>;

字符串都有了,你怕什么呢?呵呵!

后话:这个方法并不应该被推荐,因为java api下并未提供任何方法实现,也就是说我们必须在特定环境和版本下才能这样干,至少得是1.5之后的版本以后sun(现在是oracle了)准备怎么改,这个是他的说法。。。
--------------------------------------------------------------------
OK,这个是一个引子,从这里我们看到了好些个玩意,也就是java其实提供了获取的方案的
那么我们要进行私有操作的,这样对我们写代码养成这样的习惯可不好,没事就去拿私有的东西,那是不是应该有公共的方法呢?我们试一下
将main里的调用修改一下
Field c = Bean.class.getField("list");
System.out.println(c.toGenericString());

public java.util.Map<java.lang.String, org.javazone.jroi.test.reflect.ListBean> org.javazone.jroi.test.reflect.Bean.list

看看,我们拿到了什么,不需要setAccess了,呵呵

哦,对了,你或许要问如果我不是字段呢?是方法怎么办。。于是乎
package org.javazone.jroi.test.reflect;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Sandy Zhang
 */
public class Bean
{
	public Map<String, ListBean> list = new HashMap<String, ListBean>();

	public Map<String, ListBean> getList()
	{
		return list;
	}

	public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException
	{
		System.out.println(Bean.class.getMethod("getList").toGenericString());
	}
}


public java.util.Map<java.lang.String, org.javazone.jroi.test.reflect.ListBean> org.javazone.jroi.test.reflect.Bean.getList()

---------------------------------------------------------------------
字符串有了,我们考虑下,API里是不是应该也提供了方法直接获取泛型类型,参考下
getGenericType
public Type getGenericType()返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。 
如果 Type 是一个参数化类型,则返回的 Type 对象必须准确地反映源代码中使用的实际类型参数。 
如果底层字段的类型是一个类型变量或者是一个参数化类型,则创建它。否则将解析它。 
返回:
返回表示此 Field 对象所表示字段的声明类型的 Type 对象 
抛出: 
GenericSignatureFormatError - 如果一般字段签名不符合 Java Virtual Machine Specification, 3rd edition 中指定的格式 
TypeNotPresentException - 如果底层字段的一般类型签名引用了不存在的类型声明 
MalformedParameterizedTypeException - 如果底层字段的一般签名引用了一个因某种原因而无法实例化的参数化类型
从以下版本开始: 
1.5 

由此可见,在1.5之后添加了众多以Generic为关键字的方法,这些方法就是用来获取泛型参数的有效途径,但是或许表面上看不出来,因为他们都是返回的Type接口,而非ParameterizedType接口,所以我困惑了很久。OK,下面我们看一下怎么实现吧
public class GenericTest
{
	public List<String> list = new LinkedList<String>();

	public static void main(String[] args) throws SecurityException, NoSuchFieldException
	{
		ParameterizedType pt = (ParameterizedType) GenericTest.class.getField(
				"list").getGenericType();
		System.out.println(pt.getActualTypeArguments().length);
		System.out.println(pt.getActualTypeArguments()[0]);
	}
}

1
class java.lang.String
这里是结果
---------------------------------------------------------------------
于是乎,所有的问题都迎刃而解了。

似乎文章很长,我通过Debug的方式发现了代码的运行过程,最终找到了核心字符串,说明即使是檫除式的泛型java也会记录下这些东西,没道理拿不到
分享到:
评论
32 楼 agapple 2010-10-26  
以前用的获取泛型的一个方法,可恶意处理多级泛型和泛型数组。

/**
     * 取得范性信息
     *
     * @param cls
     * @param i
     * @return
     */
    private Class<?> getGenericClass(Class<?> cls, int i) {
        ParameterizedType parameterizedType = ((ParameterizedType) cls.getGenericInterfaces()[0]);
        Object genericClass = parameterizedType.getActualTypeArguments()[i];
        if (genericClass instanceof ParameterizedType) { // 处理多级泛型
            return (Class<?>) ((ParameterizedType) genericClass).getRawType();
        } else if (genericClass instanceof GenericArrayType) { // 处理数组泛型
            return (Class<?>) ((GenericArrayType) genericClass).getGenericComponentType();
        } else {
            return (Class<?>) genericClass;
        }
    }
31 楼 Relucent 2010-10-22  
如果是方法 可以用 getGenericParameterTypes 得到带泛型的类型
30 楼 zcy860511 2010-02-11  
iamlotus 写道
<div class="quote_title">myworkfirst 写道</div>
<div class="quote_div">
<div class="quote_title">RednaxelaFX 写道</div>
<div class="quote_div">
<div class="quote_title">bonny 写道</div>
<div class="quote_div">
class Domain{
public static Class getCurrentDomainClass(){
//获取运行时类型
}
}

class User() extends Domain{

}
class Test
{
@Test
public void test(){
User.getCurrentDomainClass();//。。。。无法获取User.class 
}
}

<br><br>这个功能我找了很久,貌似无法实现。</div>
<br>这个确实是不行。如果不是静态方法而是成员方法的话还有点可能,好歹能拿到“this” <br>话说这让我想起以前别人提出“为什么不弄个'static virtual method'呢?”,不过现实是Java里没这玩儿</div>
<br>   在什么场景下,要这样去获取呢? <br>
</div>
<br>比如对于Util类,按说其方法应该是static的,因为用不到this

 

 

class ParentUtil{	
	
	public <span style="color: #ff0000;">static</span> void foo(Parent p){}; 
}

class ChildUtil extends ParentUtil{	
	
	public <span style="color: #ff0000;">static</span> void foo(Parent p){}; 
}

class NeedParentUtil{
	public ParentUtil util;
	
	public void foo(){
		util.foo(new Parent()); //这里一定会调ParentUtil.foo(),即使你传进一个ChildUtil.foo()给NeedParentUtil
	}		
}

  

 

但由于static不能继承,以上的代码是无用的,所以只能写成以下这种方式 

 

 

class ParentUtil{	
	
	public void foo(Parent p){}; //为什么?明明没有用到this,我还得声明成 non static method 
}

class ChildUtil extends ParentUtil{	
	
	public  void foo(Parent p){}; 
}

class NeedParentUtil{
	public ParentUtil util;
	
	public void foo(){
		util.foo(new Parent()); 
	}		
}

 

同时,我还得寄希望于IOC容器能够保证不会new出超过一个XXUtil,占用可怜的内存。

 

根本原因嘛,为了用到继承,我就得new出一个instance来。这就是为什么会希望搞出一个'static virtual method'来的原因

 

 

<br> 

 

我不是太明白,既然是工具类,为什么要用继承的形式,这样的设计是不是欠考虑?工具类本身就是为执行特定操作而生,只是一个面向过程的概念。。。为什么要面向对象??

还有就是你说的问题可以考虑使用调用栈读取,静态类型无法获得this,但是可以在线程上做文章
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
29 楼 iamlotus 2010-02-06  
<div class="quote_title">myworkfirst 写道</div>
<div class="quote_div">
<div class="quote_title">RednaxelaFX 写道</div>
<div class="quote_div">
<div class="quote_title">bonny 写道</div>
<div class="quote_div">
<pre name="code" class="java">class Domain{
public static Class getCurrentDomainClass(){
//获取运行时类型
}
}

class User() extends Domain{

}
class Test
{
@Test
public void test(){
User.getCurrentDomainClass();//。。。。无法获取User.class
}
}
</pre>
<br><br>这个功能我找了很久,貌似无法实现。</div>
<br>这个确实是不行。如果不是静态方法而是成员方法的话还有点可能,好歹能拿到“this” <br>话说这让我想起以前别人提出“为什么不弄个'static virtual method'呢?”,不过现实是Java里没这玩儿</div>
<br>   在什么场景下,要这样去获取呢? <br>
</div>
<p><br>比如对于Util类,按说其方法应该是static的,因为用不到this</p>
<p> </p>
<p> </p>
<pre name="code" class="java">class ParentUtil{

public <span style="color: #ff0000;">static</span> void foo(Parent p){};
}

class ChildUtil extends ParentUtil{

public <span style="color: #ff0000;">static</span> void foo(Parent p){};
}

class NeedParentUtil{
public ParentUtil util;

public void foo(){
util.foo(new Parent()); //这里一定会调ParentUtil.foo(),即使你传进一个ChildUtil.foo()给NeedParentUtil
}
}</pre>
<p>  </p>
<p> </p>
<p>但由于static不能继承,以上的代码是无用的,所以只能写成以下这种方式 </p>
<p> </p>
<p> </p>
<pre name="code" class="java">class ParentUtil{

public void foo(Parent p){}; //为什么?明明没有用到this,我还得声明成 non static method
}

class ChildUtil extends ParentUtil{

public  void foo(Parent p){};
}

class NeedParentUtil{
public ParentUtil util;

public void foo(){
util.foo(new Parent());
}
}
</pre>
<p> </p>
<p>同时,我还得寄希望于IOC容器能够保证不会new出超过一个XXUtil,占用可怜的内存。</p>
<p> </p>
<p>根本原因嘛,为了用到继承,我就得new出一个instance来。这就是为什么会希望搞出一个'static virtual method'来的原因</p>
<p> </p>
<p> </p>
<p><br> </p>
<p> </p>
28 楼 myworkfirst 2010-02-04  
RednaxelaFX 写道
bonny 写道
class Domain{
public static Class getCurrentDomainClass(){
//获取运行时类型
}
}

class User() extends Domain{

}
class Test
{
@Test
public void test(){
User.getCurrentDomainClass();//。。。。无法获取User.class 
}
}


这个功能我找了很久,貌似无法实现。

这个确实是不行。如果不是静态方法而是成员方法的话还有点可能,好歹能拿到“this”
话说这让我想起以前别人提出“为什么不弄个'static virtual method'呢?”,不过现实是Java里没这玩儿

   在什么场景下,要这样去获取呢?
27 楼 rain2005 2010-02-04  
RednaxelaFX解释的相当的清晰哦,只有类型声明的时候泛型才会写入class,这里的声明包括类,方法,变量(非局部变量),运行时泛型是没有写入class的哦。


综上所述,泛型是编译期行为,但是java通过修改class字节码的方式让程序在运行时可以通过反射来获取类型信息。
26 楼 chenzengpeng 2010-02-04  
以前获得Class中的泛型类型都是用如下方法:
class BaseService<T extends BaseEntity>
{
  ...
  public Class<T> getClazz() {
        ParameterizedType parameterizedType = (ParameterizedType).getClass().getGenericSuperclass();
        return (Class<T>) parameterizedType.getActualTypeArguments()[0];
    }
}
  
25 楼 zcy860511 2010-02-04  
貌似Class的类型通过Debug我也没发现哪有记录,不知道哪个大牛能拿出来,看看
24 楼 luojy8877 2010-02-03  
利用反射获取泛型  学习了 
23 楼 RednaxelaFX 2010-02-03  
AlwenS 写道
本贴的亮点在于这句话:

RednaxelaFX 写道

知道了什么信息有记录,什么信息没有记录之后,也就可以省点力气不去纠结“拿不到T的实际类型”、“建不出T类型的数组”之类的问题了orz


只是 [什么时候记录,什么时候不记录] 的语义上的区分还有些不明确.

声明一侧与使用一侧的两条还是不够用么,呜 TvT
这个要详细写就能赶超楼主写的长度了……去读读Java 5的class文件格式基本上该知道的也就都知道了。
22 楼 linliangyi2007 2010-02-03  
大牛出现,ORZ了,学习一下,温故知新。
21 楼 AlwenS 2010-02-03  
本贴的亮点在于这句话:

RednaxelaFX 写道

知道了什么信息有记录,什么信息没有记录之后,也就可以省点力气不去纠结“拿不到T的实际类型”、“建不出T类型的数组”之类的问题了orz


只是 [什么时候记录,什么时候不记录] 的语义上的区分还有些不明确.
20 楼 phz50 2010-02-03  
推荐看看SpringSide,相当不错!
19 楼 andot 2010-02-03  
gwpking8419 写道
andot 写道
askyuan 写道
抄袭的吧?呵呵

自己写不出来,就说人家抄袭,真卑鄙!真无耻!真龌龊!

楼主的这篇文章实在是太好了,让我如醍醐灌顶,茅塞顿开,解决了我多年来的疑惑!感谢楼主!



文明用语,别给中国人丢脸!


这句话应该留给你自己,你们这些喷子是最丢人的!
18 楼 zgz888 2010-02-03  
高手,回去好好学学
17 楼 RednaxelaFX 2010-02-03  
kjj 写道
如果是T
怎么在运行期取到类型呢,这个可以吗!!

其实前面才刚刚提到这个问题……
16 楼 caoyangx 2010-02-03  
这种java基础的文章很多,谈不上抄袭,每个人都有自己的理解。
15 楼 kjj 2010-02-03  
如果是T
怎么在运行期取到类型呢,这个可以吗!!
14 楼 aoliwen521 2010-02-03  
楼主写的很有想法,我以前从没想过这些事情。
比很多人炒旧饭要强多了。
13 楼 bonny 2010-02-03  
RednaxelaFX 写道
bonny 写道
class Domain{
public static Class getCurrentDomainClass(){
//获取运行时类型
}
}

class User() extends Domain{

}
class Test
{
@Test
public void test(){
User.getCurrentDomainClass();//。。。。无法获取User.class 
}
}


这个功能我找了很久,貌似无法实现。

这个确实是不行。如果不是静态方法而是成员方法的话还有点可能,好歹能拿到“this”
话说这让我想起以前别人提出“为什么不弄个'static virtual method'呢?”,不过现实是Java里没这玩儿


这玩意太有用了,我看play框架,因为java无法提供这个功能,他的find都是直接丢异常。子类的model都是classload时做字节码增强。其实跟很丑的roo的做法一摸一样,太丑了。

相关推荐

    java 泛型类的类型识别示例

    综上所述,虽然Java泛型在编译后会进行类型擦除,但通过上述技巧,我们仍然能够在运行时获得关于泛型类实例化类型的一些信息。在实际开发中,这些方法可以帮助我们编写更加灵活和安全的代码。在示例文件`GenericRTTI...

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

    Java泛型是Java编程语言中的一种重要特性,它允许开发者在编写代码时指定类型参数,从而提高代码的灵活性和可读性。本文将详细介绍Java泛型的用法 及T.class的获取过程解析。 一、泛型的基本概念 泛型是Java 5中...

    java 泛型接口示例

    因此,虽然在编译期间我们能获得类型检查的好处,但在运行时,泛型接口和类的行为与无参数类型版本基本相同。 5. **通配符** 在某些情况下,我们可能不关心类型参数的具体类型,而只关心它是某个类的子类或者实现...

    Java1.5泛型指南中文版

    3. **类型擦除**:Java的泛型在编译后会执行类型擦除,这意味着在运行时,所有的泛型类型信息都会丢失。因此,虽然在编译时提供了类型检查,但在运行时,泛型对象仍然是Object类型。这就是为什么你仍然可以将任何...

    Java泛型技术之发展

    Java泛型技术的发展不仅标志着编程语言对类型安全和代码复用的重视,也反映了软件工程领域对模块化、复用性和维护性的不断追求。自JDK1.4以来,泛型技术已成为Java开发不可或缺的一部分,极大地提升了开发效率和代码...

    JAVA的泛型指南

    - **泛型擦除的影响**:运行时不能获取类型参数的具体类型,也无法实例化泛型类型参数。 - **类型参数的限制**:类型参数不能用于静态上下文中,因为静态成员与类关联而非对象关联。 #### 结语 通过本文的介绍,...

    详谈Java8新特性泛型的类型推导

    例如,以下代码在Java 7中是不合法的,因为编译器无法从上下文中推断出`ArrayList`的泛型类型: ```java List&lt;String&gt; list = new ArrayList(); list.add("A"); list.addAll(new ArrayList()); ``` 在Java 8中,...

    java泛型类和函数

    在Java中,泛型是一种允许开发者在类、接口和方法中使用类型参数的功能。通过使用泛型,可以在编写代码时指定一个或多个类型参数,从而使得编写的代码更加灵活且重用性更高。这种机制在Java 5中被引入,并在集合框架...

    Java基础入门四泛型反射注解.pdf

    泛型是Java SE 5版本引入的一个新特性,它的主要目的是允许在使用类、接口和方法时能够引用到任何类型的对象,同时在编译期间就能进行类型检查,提供更安全的代码。泛型类和泛型方法可以提高代码的复用性,并减少...

    Java编程语言中的泛型

    虽然Java编译器在编译期间检查泛型类型的有效性,但在生成字节码时会进行所谓的“类型擦除”过程。这意味着在运行时,泛型类型信息会被抹去,所有泛型类型都会转换成其对应的原始类型。 例如,`List&lt;String&gt;` 和 `...

    Java1_5泛型.zip

    8. 泛型集合:泛型引入后,Java 集合框架得到了极大的增强。使用泛型集合,可以避免在添加、删除和访问元素时出现 ClassCastException。 9. 泛型和多态:泛型可以与多态结合使用,使代码更加灵活。例如,一个接受 `...

    java泛型编程

    这意味着所有泛型类型最终都会被转换为非泛型类型。例如,`List&lt;String&gt;`和`List&lt;Integer&gt;`在运行时实际上是相同的类型,都被擦除成了`List`。 ##### 类型擦除的影响 - **静态成员问题**:泛型类中的静态成员无法...

    Java泛型深入研究

    Java 泛型是Java 5引入的一个重要特性,它允许在类、接口和方法中使用类型参数,从而提高了代码的重用性和安全性。泛型的主要目的是在编译时检查类型安全,并且允许程序员以更方便的方式操作集合。 1. **无泛型的...

    泛型JAVA指南.pdf

    例如,类型擦除(Erasure)是Java泛型的一个重要概念,意味着泛型信息在编译后将被擦除,使得在运行时无法获得泛型类型的详细信息。因此,不能使用instanceof检查泛型类型的参数化形式,只能检查其擦除形式,例如...

    Java泛型学习笔记.pdf

    例如,如果泛型类中的方法接受或返回泛型类型,那么应该考虑使用类型参数来定义这些方法,以保持类型安全性。 7. 通配符的使用场景: 通配符在读取时使用非常方便,因为读者不需要关心具体的类型。但在写入时就有...

    Java 泛型教程含示例代码

    3. 一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。 4. 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。 5. 泛型方法体的声明和其他方法一样。 注意...

Global site tag (gtag.js) - Google Analytics