如何获得实际类型参数到间接实现的通用接口? [英] How to get the actual type arguments to an indirectly implemented generic interface?

查看:114
本文介绍了如何获得实际类型参数到间接实现的通用接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个参数化的接口,它以许多不同的方式实现。在运行时,我需要弄清楚,给定一个实现该接口的任意对象,接口的实际类型参数是什么。



这里有一个片段来说明问题,并试图解决它(也在ideone.com 上):

  import java.util。*; 
import java.lang.reflect。*;

界面真棒< X> {}
class Base< E>实现Awesome< Set< E>> {}
class Child extends Base< List< Integer>> {}

class AwesomeExample {
public static void main(String [] args){
Awesome< Set< List< Integer>>> x = new Child();

System.out.println(
((ParameterizedType)
Child.class.getGenericSuperclass()$ b $).getActualTypeArguments()[0]
) ;
//打印java.util.List< java.lang.Integer>

System.out.println(
((ParameterizedType)
Base.class.getGenericInterfaces()[0]
).getActualTypeArguments()[0]
);
//打印java.util.Set< E>

调查(x);
//我们希望这可以打印Set< List< Integer>>
}

static void investigations(Awesome<?> somethingAwesome){
//如何做到这一点?




$ b看起来在运行时有足够的泛型类型信息推断:


  • Child extends Base< List< Integer>> li>
  • Base< E>实现Awesome< Set< E>>



因此,我们可以将所有零碎拼凑在一起得出结论:


  • 孩子实现了Awesome< Set< List< Integer>>>

    所以看起来问题是可以解决的,但并不是那么简单,因为我们必须使用任意的类/接口层次结构。这是做到这一点的唯一方法吗?有一种更简单的方法吗?有人写过一个库来做到这一点吗?解决方案

编辑:你可能只是想使用: http://code.google.com/p/gentyref/



如果您可以保证 Awesome <?>< / code>的所有实现都不会有类型参数,那么下面的代码会让您开始[1]: p>

  static void investigations(Object o){
final Class<?> c = o.getClass();
System.out.println(\\\
+ c.getName()+implements:);
调查(c,(Type [])null);


static void investig(Type t,Type ... typeArgs){
if(t == null)return; $($)
$ b(b)如果(t的实例类型<>){
investig((Class<> t,typeArgs);
} else if(t instanceof ParameterizedType){
investig((ParameterizedType)t,typeArgs);



static void investigation(Class <?> c,Type ... typeArgs){
investigation(c.getGenericSuperclass(),typeArgs) ;

for(Type i:c.getGenericInterfaces()){
investigation(i,typeArgs);
}
}

static void investig(ParameterizedType p,Type ... typeArgs){
final Class<?> c =(Class<>)p.getRawType();
final StringBuilder b = new StringBuilder(c.getName());
b.append('<');
类型[] localArgs = p.getActualTypeArguments();
if(typeArgs!= null&& typeArgs.length> 0){
int i = 0,nextTypeArg = 0;
for(Type local:localArgs){
if(local instanceof ParameterizedType){
ParameterizedType localP =(ParameterizedType)local;
b.append(localP.getRawType())。append('<');
b.append(typeArgs [nextTypeArg ++]);
b.append('>');
} else if(local instanceof TypeVariable){
//将本地类型arg指定为一个实例。
localArgs [nextTypeArg] = typeArgs [nextTypeArg];
b.append(localArgs [nextTypeArg]);
nextTypeArg ++;
} else {
b.append(local.toString());
}
b.append(,);
i ++;

if(typeArgs.length> 0){
b.delete(b.length() - 2,b.length());
}
b.append('>');
} else {
String args = Arrays.toString(localArgs);
b.append(args.substring(1,args.length() - 1))。append('>');
}
System.out.println(b);
调查(c,localArgs);
}

然而,如果实例化 Awesome <?> ; Base< E> 将被删除,该类型信息将因删除而丢失。作为一个惯例,这可以像下面这样工作:

  Awesome<?> awesome = new Base< Double>(){}; 

注意 {} ,这会创建一个实现(或扩展) Base< E> 的新匿名类。这个类将有它的类型参数可用于反射。



如果您害怕执行这个约定将是一个问题,您可以隐藏构造函数&只显示工厂方法:

  class Base< E>实现Awesome< Set< E>> {

public static Base< Number> newNumberInstance(){
返回新的Base< Number> (){};


保护Base(){}
}



由于上述代码尚未完全测试,您可能需要这样做。这里的要点是,如果您的要求足够严格,您可以找到实际的类型参数。是否适用于您的情况取决于您的情况。


[1]它将打印出一个类实现的所有接口&不只是 Awesome 的类型参数。这可以改变,但我想我会去更通用&让你制定具体的细节。例如,您需要测试这些以了解我的意思:

 调查(新的ArrayList<整数>()) ; 
调查(新的ArrayList< String>(){}); //新的匿名ArrayList类
调查();
调查(新的Awesome< Comparable<>>(){}); // Awesome
的新匿名实现


I have a parameterized interface that is implemented in many different ways. At run time I need to figure out, given an arbitrary object that implements that interface, what the actual type parameters to the interface is.

Here's a snippet to illustrate the problem, and a halfway attempt to solve it (also on ideone.com):

import java.util.*;
import java.lang.reflect.*;

interface Awesome<X> { }
class Base<E> implements Awesome<Set<E>> { }
class Child extends Base<List<Integer>> { }

class AwesomeExample {      
    public static void main(String[] args) {
        Awesome<Set<List<Integer>>> x = new Child();

        System.out.println(
            ((ParameterizedType)
                Child.class.getGenericSuperclass()
            ).getActualTypeArguments()[0]
        );
        // prints "java.util.List<java.lang.Integer>"

        System.out.println(
            ((ParameterizedType)
                Base.class.getGenericInterfaces()[0]
            ).getActualTypeArguments()[0]
        );
        // prints "java.util.Set<E>"        

        investigate(x);
        // we want this to print "Set<List<Integer>>"
    }

    static void investigate(Awesome<?> somethingAwesome) {
        // how to do this?
    }
}

It looks like there's enough generic type information at runtime to deduce that:

  • Child extends Base<List<Integer>>
  • Base<E> implements Awesome<Set<E>>

And therefore we can put all the bits and pieces together to conclude that:

  • Child implements Awesome<Set<List<Integer>>>

So it looks like the problem is solvable, but it's not that simple, since we'd have to work with an arbitrary class/interface hierarchy. Is this the only way to do this? Is there a simpler way? Has someone written a library to do this already?

解决方案

Edit: You may just want to look into using: http://code.google.com/p/gentyref/

If you can guarantee that all implementations of Awesome<?> will not have type arguments, the following code should get you started [1]:

static void investigate(Object o) {
    final Class<?> c = o.getClass();
    System.out.println("\n" + c.getName() + " implements: ");
    investigate(c, (Type[])null);
}

static void investigate(Type t, Type...typeArgs) {
    if(t == null) return;

    if(t instanceof Class<?>) {
        investigate((Class<?>)t, typeArgs);
    } else if(t instanceof ParameterizedType) {
        investigate((ParameterizedType)t, typeArgs);
    }
}

static void investigate(Class<?> c, Type...typeArgs) {
    investigate(c.getGenericSuperclass(), typeArgs);

    for(Type i : c.getGenericInterfaces()) {
        investigate(i, typeArgs);
    }
}

static void investigate(ParameterizedType p, Type...typeArgs) {
    final Class<?> c = (Class<?>)p.getRawType();
    final StringBuilder b = new StringBuilder(c.getName());
    b.append('<');
    Type[] localArgs = p.getActualTypeArguments();
    if(typeArgs != null && typeArgs.length > 0) {
        int i = 0, nextTypeArg = 0;
        for(Type local : localArgs) {
            if(local instanceof ParameterizedType) {
                ParameterizedType localP = (ParameterizedType) local;
                b.append(localP.getRawType()).append('<');
                b.append(typeArgs[nextTypeArg++]);
                b.append('>');
            } else if(local instanceof TypeVariable) {
                // reify local type arg to instantiated one.
                localArgs[nextTypeArg] = typeArgs[nextTypeArg];
                b.append(localArgs[nextTypeArg]);
                nextTypeArg++;
            } else {
                b.append(local.toString());
            }
            b.append(", ");
            i++;
        }
        if(typeArgs.length > 0) {
            b.delete(b.length() - 2, b.length());
        }
        b.append('>');
    } else {
        String args = Arrays.toString(localArgs);
        b.append(args.substring(1, args.length()-1)).append('>');
    }
    System.out.println(b);
    investigate(c, localArgs);
}

If, however, instantiations of Awesome<?> or Base<E> will be made, that type information will be lost due to erasure. This can be worked around, as a convention, with something like this:

Awesome<?> awesome = new Base<Double>() {};

Notice the {}, this creates a new anonymous class that implements (or here extends) Base<E>. This class will have its type parameters available for reflection.

If you're afraid enforcing this convention will be an issue, you can hide the constructors & only expose factory methods:

class Base<E> implements Awesome<Set<E>> {

    public static Base<Number> newNumberInstance() {
        return new Base<Number> () {};
    }

    protected Base() {}
}

As the above code has not been completely tested, you may want to do that. The point here is that you can find the actual type parameters given your requirements are stringent enough. Whether or not that applies to your situation is up to you to determine.

[1] It will print out all of the interfaces a class implements & not just the type parameters of Awesome. This can be changed, but I figured I'd go for more general & let you work out the specifics. For example, you'll want to test these to see what I mean:

investigate(new ArrayList<Integer>());
investigate(new ArrayList<String>() {}); // new anonymous ArrayList class
investigate("");
investigate(new Awesome<Comparable<?>> () {}); // new anonymous implementation of Awesome

这篇关于如何获得实际类型参数到间接实现的通用接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆