Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/306.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
泛型和java.beans.Introspector_Java_Generics_Introspection_Javabeans - Fatal编程技术网

泛型和java.beans.Introspector

泛型和java.beans.Introspector,java,generics,introspection,javabeans,Java,Generics,Introspection,Javabeans,给定以下代码框架,是否可以确定属性foo实际上是类型String public class TestIntrospection { public static class SuperBean<T> { private T foo; public T getFoo() { return foo; } public void setFoo(T foo) { this.foo = foo; } } public s

给定以下代码框架,是否可以确定属性
foo
实际上是类型
String

public class TestIntrospection {
    public static class SuperBean<T> {
        private T foo;

        public T getFoo() { return foo; }
        public void setFoo(T foo) { this.foo = foo; }
    }

    public static class SubBean extends SuperBean<String> {
    }

    public static void main(String[] args) throws IntrospectionException {
        BeanInfo beanInfo = Introspector.getBeanInfo(SubBean.class);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor prop : propertyDescriptors) {
            if ("foo".equals(prop.getName())) {
                System.out.printf("%s of %s\n", prop.getName(), prop.getPropertyType());

                Method readMethod = prop.getReadMethod();
                Type returnType = prop.getReadMethod().getGenericReturnType();
                if (returnType instanceof TypeVariable) {
                    TypeVariable t = (TypeVariable) returnType;
                    GenericDeclaration d = t.getGenericDeclaration();
                    System.out.println("TypeVariable : " + t.getName() + " " + t.getBounds()[0]);
                }
            }
        }
    }
}
输出:

类java.lang.String


接下来的问题是,如何将这个实际的类型参数与类型变量关联起来?如果我知道只有一个类型参数,这很简单,但我希望这段代码也适用于具有多个泛型类型参数的bean。

Java泛型在编译期间会经历类型擦除。在运行时,无法确定编译期间出现的T的类型

这里有一个链接:

不幸的是,没有:

泛型是通过类型擦除来实现的:泛型类型信息只在编译时出现,然后由编译器擦除。这种方法的主要优点是,它提供了通用代码和使用非参数化类型(技术上称为原始类型)的遗留代码之间的完全互操作性。主要缺点是参数类型信息在运行时不可用,并且在与行为不正确的遗留代码进行互操作时,自动生成的强制转换可能会失败。但是,有一种方法可以为泛型集合实现有保证的运行时类型安全,即使在与行为不良的遗留代码进行互操作时也是如此


所述,您可以通过获取运行时类型的泛型。从链接中提取的代码

    public class Base<T> {

      private final Class<T> klazz;

      @SuppressWarnings("unchecked")
      public Base() {
        Class<? extends Base> actualClassOfSubclass = this.getClass();
        ParameterizedType parameterizedType = (ParameterizedType) actualClassOfSubclass.getGenericSuperclass();
        Type firstTypeParameter = parameterizedType.getActualTypeArguments()[0];
        this.klazz = (Class) firstTypeParameter;
      }

      public boolean accepts(Object obj) {
        return this.klazz.isInstance(obj);
      }

    } 

    class ExtendsBase extends Base<String> {

      // nothing else to do!

    }
public class ExtendsBaseTest {

  @Test
  public void testTypeDiscovery() {
    ExtendsBase eb = new ExtendsBase();
    assertTrue(eb.accepts("Foo"));
    assertFalse(eb.accepts(123));
  }
}
公共类基{
私人期末班克拉兹;
@抑制警告(“未选中”)
公共基地(){

Class这是
SuperBean的字节码:

public class foo.bar.SuperBean {

  // Field descriptor #6 Ljava/lang/Object;
  // Signature: TT;
  private java.lang.Object foo;

  // Method descriptor #10 ()V
  // Stack: 1, Locals: 1
  public SuperBean();
    0  aload_0 [this]
    1  invokespecial java.lang.Object() [12]
    4  return
      Line numbers:
        [pc: 0, line: 3]
      Local variable table:
        [pc: 0, pc: 5] local: this index: 0 type: foo.bar.SuperBean
      Local variable type table:
        [pc: 0, pc: 5] local: this index: 0 type: foo.bar.SuperBean<T>

  // Method descriptor #21 ()Ljava/lang/Object;
  // Signature: ()TT;
  // Stack: 1, Locals: 1
  public java.lang.Object getFoo();
    0  aload_0 [this]
    1  getfield foo.bar.SuperBean.foo : java.lang.Object [24]
    4  areturn
      Line numbers:
        [pc: 0, line: 8]
      Local variable table:
        [pc: 0, pc: 5] local: this index: 0 type: foo.bar.SuperBean
      Local variable type table:
        [pc: 0, pc: 5] local: this index: 0 type: foo.bar.SuperBean<T>

  // Method descriptor #27 (Ljava/lang/Object;)V
  // Signature: (TT;)V
  // Stack: 2, Locals: 2
  public void setFoo(java.lang.Object foo);
    0  aload_0 [this]
    1  aload_1 [foo]
    2  putfield foo.bar.SuperBean.foo : java.lang.Object [24]
    5  return
      Line numbers:
        [pc: 0, line: 12]
        [pc: 5, line: 13]
      Local variable table:
        [pc: 0, pc: 6] local: this index: 0 type: foo.bar.SuperBean
        [pc: 0, pc: 6] local: foo index: 1 type: java.lang.Object
      Local variable type table:
        [pc: 0, pc: 6] local: this index: 0 type: foo.bar.SuperBean<T>
        [pc: 0, pc: 6] local: foo index: 1 type: T
}
公共类foo.bar.SuperBean{
//字段描述符#6 Ljava/lang/Object;
//签字:TT;
私有java.lang.Object foo;
//方法描述符#10()V
//堆栈:1,局部变量:1
公共超级豆();
0 aload_0[此]
1调用特殊的java.lang.Object()[12]
4返回
行号:
[个人计算机:0,第3行]
局部变量表:
[pc:0,pc:5]本地:此索引:0类型:foo.bar.SuperBean
局部变量类型表:
[pc:0,pc:5]本地:此索引:0类型:foo.bar.SuperBean
//方法描述符#21()Ljava/lang/Object;
//签字:()TT;
//堆栈:1,局部变量:1
public java.lang.Object getFoo();
0 aload_0[此]
1 getfield foo.bar.SuperBean.foo:java.lang.Object[24]
4轮
行号:
[个人计算机:0,第8行]
局部变量表:
[pc:0,pc:5]本地:此索引:0类型:foo.bar.SuperBean
局部变量类型表:
[pc:0,pc:5]本地:此索引:0类型:foo.bar.SuperBean
//方法描述符#27(Ljava/lang/Object;)V
//签字:(TT;)五
//堆栈:2,局部变量:2
public void setFoo(java.lang.Object foo);
0 aload_0[此]
1 aload_1[foo]
2 putfield foo.bar.SuperBean.foo:java.lang.Object[24]
5返回
行号:
[个人计算机:0,第12行]
[pc:5,第13行]
局部变量表:
[pc:0,pc:6]本地:此索引:0类型:foo.bar.SuperBean
[pc:0,pc:6]本地:foo索引:1类型:java.lang.Object
局部变量类型表:
[pc:0,pc:6]本地:此索引:0类型:foo.bar.SuperBean
[pc:0,pc:6]本地:foo索引:1类型:T
}

如您所见,getter和setter都是java.lang.Object类型。Introspector使用getter和setter生成PropertyDescriptor(字段被忽略),因此属性无法知道T的泛型类型。

不幸的是,类型擦除是完全有效的

尽管子bean似乎应该为该ivar和那些方法提供固定类型的字符串,因为
SuperBean
的类型参数在编译时是已知的,但不幸的是,这不是它的工作方式。编译器不会在编译时为
子bean创建
String
-ified版本的
SuperBean
要从中派生-只有一个(类型已擦除)
SuperBean

然而,我遇到的一个可能很糟糕的解决方法是,
SubBean
可能能够用特定类型的版本覆盖超类方法,然后BeanInfo可能会返回您期望的方法:

public static class SubBean
extends SuperBean<String> {
    // Unfortunate this is necessary for bean reflection ...
    public String getFoo()         { return super.getFoo(); }
    public void setFoo(String foo) { super.setFoo(foo); }
}
公共静态类子bean
扩展超级豆{
//不幸的是,这是bean反射所必需的。。。
公共字符串getFoo(){return super.getFoo();}
public void setFoo(字符串foo){super.setFoo(foo);}
}

更新:上述内容无效。请注意@Jörn Horstmann在评论中发布的以下信息:


这似乎不起作用,因为内省程序仍在返回Object类型的read方法。此外,这似乎是一个生成的桥接方法(),这意味着如果要访问此方法上的注释,可能会遇到此方法

上述解决方案的另一个丑陋的变体是将属性别名为:

public static class SubBean
extends SuperBean<String> {
    // Unfortunate this is necessary for bean reflection ...
    public String getFooItem()         { return super.getFoo(); }
    public void setFooItem(String foo) { super.setFoo(foo); }
}
公共静态类子bean
扩展超级豆{
//不幸的是,这是bean反射所必需的。。。
公共字符串getFooItem(){return super.getFoo();}
public void setFooItem(字符串foo){super.setFoo(foo);}
}

SubBean
现在有了一个独特的属性
FooItem
,它是原始
SuperBean的别名
property
Foo

我找到了一个解决方案,解决了层次结构中只有一个super的情况 类(对象除外),该类在超级类上有多个类型参数时也有效

仍然不适用于更深层的层次结构或实现泛型 接口。此外,我想确认这是事实上记录的 而且应该有用

public static class SuperBean<F, B, Q> {
    // getters and setters
}

public static class SubBean<X> extends SuperBean<String, Integer, X> {
}
公共静态类SuperBean{
//接球手和接球手
}
公共静态类子bean扩展了SuperBean{
}

Type returnType=readMethod.getGenericReturnType();
public static class SuperBean<F, B, Q> {
    // getters and setters
}

public static class SubBean<X> extends SuperBean<String, Integer, X> {
}
                Type returnType = readMethod.getGenericReturnType();

                Type superClass = SubBean.class.getGenericSuperclass();
                GenericDeclaration genericDecl = ((TypeVariable) returnType).getGenericDeclaration();
                TypeVariable[] parameters = genericDecl.getTypeParameters();
                Type[]         actualArgs = ((ParameterizedType) superClass).getActualTypeArguments();

                for (int i=0; i<parameters.length; i++) {
                    //System.out.println(parameters[i] + " " + actualArgs[i]);
                    if (returnType == parameters[i]) {
                        System.out.println("Match : " + parameters[i] + " : " + actualArgs[i]);
                    }
                }
class Substitution extends HashMap<String, TypeExpr> {
    Substitution(TypeVariable[] formals, TypeExpr[] actuals) {
        for (int i = 0; i < actuals.length; i++) {
            put(formals[i].getName(),actuals[i]);
        }
    }

}

abstract class TypeExpr {
    abstract TypeExpr apply(Substitution s);

    public abstract String toString(); 

    static TypeExpr from(Type type) {
        if (type instanceof TypeVariable) {
            return new TypeVar((TypeVariable) type);
        } else if (type instanceof Class) {
            return new ClassType((Class) type);
        } else if (type instanceof ParameterizedType) {
            return new ClassType((ParameterizedType) type);
        } else if (type instanceof GenericArrayType) {
            return new ArrayType((GenericArrayType) type);
        } else if (type instanceof WildcardType) {
            return new WildcardTypeExpr((WildcardType) type);
        }
        throw new IllegalArgumentException(type.toString());
    }

    static TypeExpr[] from(Type[] types) {
        TypeExpr[] t = new TypeExpr[types.length];
        for (int i = 0; i < types.length; i++) {
            t[i] = from(types[i]);
        }
        return t;
    }

    static TypeExpr[] apply(TypeExpr[] types, Substitution s) {
        TypeExpr[] t = new TypeExpr[types.length];
        for (int i = 0; i < types.length; i++) {
            t[i] = types[i].apply(s);
        }
        return t;
    }

    static void append(StringBuilder sb, String sep, Object[] os) {
        String s = "";
        for (Object o : os) {
            sb.append(s);
            s = sep;
            sb.append(o);
        }
    }
}

class TypeVar extends TypeExpr {
    final String name;

    public TypeVar(String name) {
        this.name = name;
    }

    public TypeVar(TypeVariable var) {
        name = var.getName();
    }

    @Override
    public String toString() {
        return name;
    }

    @Override
    TypeExpr apply(Substitution s) {
        TypeExpr e = s.get(name);
        return e == null ? this : e;
    }
}

class ClassType extends TypeExpr {
    final Class clazz;
    final TypeExpr[] arguments; // empty if the class is not generic

    public ClassType(Class clazz, TypeExpr[] arguments) {
        this.clazz = clazz;
        this.arguments = arguments;
    }

    public ClassType(Class clazz) {
        this.clazz = clazz;
        arguments = from(clazz.getTypeParameters());
    }

    @Override
    public String toString() {
        String name = clazz.getSimpleName();
        if (arguments.length == 0) {
            return name;
        }

        StringBuilder sb = new StringBuilder();
        sb.append(name);
        sb.append("<");
        append(sb, ", ", arguments);
        sb.append(">");
        return sb.toString();
    }

    public ClassType(ParameterizedType pt) {
        clazz = (Class) pt.getRawType();
        Type[] args = pt.getActualTypeArguments();
        arguments = TypeExpr.from(args);
    }

    @Override
    ClassType apply(Substitution s) {
        return new ClassType(clazz, apply(arguments, s));
    }
}

class ArrayType extends TypeExpr {
    final TypeExpr componentType;

    public ArrayType(TypeExpr componentType) {
        this.componentType = componentType;
    }

    public ArrayType(GenericArrayType gat) {
        this.componentType = TypeExpr.from(gat.getGenericComponentType());
    }

    @Override
    public String toString() {
        return componentType + "[]";
    }

    @Override
    TypeExpr apply(Substitution s) {
        return new ArrayType(componentType.apply(s));
    }
}

class WildcardTypeExpr extends TypeExpr {
    final TypeExpr[] lowerBounds;
    final TypeExpr[] upperBounds;

    public WildcardTypeExpr(TypeExpr[] lowerBounds, TypeExpr[] upperBounds) {
        this.lowerBounds = lowerBounds;
        this.upperBounds = upperBounds;
    }

    WildcardTypeExpr(WildcardType wct) {
        lowerBounds = from(wct.getLowerBounds());
        upperBounds = from(wct.getUpperBounds());
    }

    @Override
    TypeExpr apply(Substitution s) {
        return new WildcardTypeExpr(
            apply(lowerBounds, s), 
            apply(upperBounds, s)
        );
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("?");
        if (lowerBounds.length > 0) {
            sb.append(" super ");
            append(sb, " & ", lowerBounds);
        }
        if (upperBounds.length > 0) {
            sb.append(" extends ");
            append(sb, " & ", upperBounds);
        }
        return sb.toString();
    }
}

public class Test {

    /**
     * @return {@code superClazz}, with the replaced type parameters it has for
     *         instances of {@code ct}, or {@code null}, if {@code superClazz}
     *         is not a super class or interface of {@code ct}
     */
    static ClassType getSuperClassType(ClassType ct, Class superClazz) {
        if (ct.clazz == superClazz) {
            return ct;
        }

        Substitution sub = new Substitution(ct.clazz.getTypeParameters(), ct.arguments);

        Type gsc = ct.clazz.getGenericSuperclass();
        if (gsc != null) {
            ClassType sct = (ClassType) TypeExpr.from(gsc);
            sct = sct.apply(sub);
            ClassType result = getSuperClassType(sct, superClazz);
            if (result != null) {
                return result;
            }
        }

        for (Type gi : ct.clazz.getGenericInterfaces()) {
            ClassType st = (ClassType) TypeExpr.from(gi);
            st = st.apply(sub);
            ClassType result = getSuperClassType(st, superClazz);
            if (result != null) {
                return result;
            }

        }
        return null;
    }

    public static ClassType getSuperClassType(Class clazz, Class superClazz) {
        return getSuperClassType((ClassType) TypeExpr.from(clazz), superClazz);
    }
    public static void check(Class c, Class sc, String expected) {
        String actual = getSuperClassType(c, sc).toString();
        if (!actual.equals(expected)) {
            throw new AssertionError(actual + " != " + expected);
        }
    }

    public static void main(String[] args) {
        check(Substitution.class, Map.class, "Map<String, TypeExpr>");
        check(HashMap.class, Map.class, "Map<K, V>");
        check(Bar.class, Foo.class, "Foo<List<? extends String[]>>");
    }
}

interface Foo<X> {

}
class SuperBar<X, Y> implements Foo<List<? extends Y[]>> {

}

class Bar<X> extends SuperBar<X, String> { }
class Super<T> {
    final Class<T> clazz;

    T foo;

    Super(Class<T> clazz) {
        this.clazz = clazz;
    }

    public T getFoo() {
        return foo;
    }

    public T setFoo() {
        this.foo = foo;
    }
}