【问题标题】:How to read property from Class<T> instance in Java?如何从 Java 中的 Class<T> 实例中读取属性?
【发布时间】:2020-11-10 10:50:29
【问题描述】:

我有一个方法methodA(),它将Class&lt;T&gt; inClass 类的实例作为参数。

methodA() 中传递的任何给定inClass 实例都将具有一个名为user:String 的属性。

所以我想在methodA() 中读取该属性。但我得到Cannot resolve method 'getUser' in 'T'

是否有可能以某种方式从泛型类中读取属性?

或者,我可以在methodA() 中将我想要的类的名称作为字符串传递并以某种方式将其转换为“ClassName”.class 吗?

private <T> Function<PredicateSpec, Route.AsyncBuilder> walletRoute(String server, String path, String method, Class<T> inClass) {
    return r -> r.path(path)
                 .and().method(method)
                 .and().readBody(inClass, requestBody ->
                    walletDiscoveryService(requestBody.getUser()).equals(server))
                 .uri("http://" + server);
}

我调用如下方法:

walletRoute("localhost:3000", "/api/account/**", "POST", BankAccount.class)

【问题讨论】:

  • 你应该发布你的代码。
  • Class 怎么样,其中的类 U 是抽象的并且声明了 getUser?
  • 不行,除非你给T的具体实例也通过了。 Class&lt;T&gt; 类型的对象本身并不引用 T 的任何特定实例。
  • @Fureeish,你能告诉我怎么做吗?当我调用该方法时,我确实将 Account.class 或 BankAccount.class 作为参数传递,它们都具有我想要读取的属性
  • 您确定要通过 Class 而不是 T 吗?

标签: java oop kotlin generics java-8


【解决方案1】:

如何从 Java 中的 Class 实例中读取属性?

自己?对象属性?你不能


想想一个对象。一个简单的 Java 对象。它拥有它的所有成员,也许还有一些额外的、继承的东西。假设我们有这个简单的类:

class Foo {
    String name;
}

创建Foo 类型的对象后,我们可以像这样访问它的内部:

Foo f = new Foo();
f.name = "Groot";
System.out.println(f.name);

漂亮又简单。


现在让我们介绍一些Class&lt;T&gt;魔术:

Foo f = new Foo();
f.name = "Groot";
System.out.println(f.name);

Class<Foo> fClass = Foo.class;

太好了,我们有一个Class 实例。我们可以用它做什么?

在我们回答这个问题之前,请注意fClass 的创建f 实例无关(不知道,不需要知道)

这是什么意思?这意味着它绝不会绑定到f。它绑定到fclass,但它不知道f 本身。您无法从fClass 中检索name,因为随后会检索到哪个name

要完全理解这个问题,请考虑以下代码:

Foo f1 = new Foo();
f1.name = "Groot";
Foo f2 = new Foo();
f2.name = "Rocket";

Class<Foo> fClass = Foo.class;
//fClass.name ???

如果 fClass 可以从Foo 类型的某个对象引用name,它会引用哪个namef1 还是 f2?你不知道。


这就是为什么Class&lt;T&gt; 做了一些不同的事情。它使您能够获得字段的通用表示(例如,Fooname),但它不会产生具体值(参考),除非您指定该字段必须反映的对象(这称为反射)。看看:

Foo f1 = new Foo();
f1.name = "Groot";
Foo f2 = new Foo();
f2.name = "Rocket";

Class<Foo> fClass = Foo.class;

try {
    Field nameField = fClass.getDeclaredField("name");
    System.out.println(nameField.get(f2));

} catch (NoSuchFieldException | IllegalAccessException e) {
    e.printStackTrace();
}

这是一个完整的sn-p。花点时间消化一下。

当您尝试访问name 字段时,魔法就会发生。注意Field 类型。在本例中,它是一个对象,表示对名为"name" 的字段的访问。但要小心 - 我们可能拼错了字段名称,这就是为什么我们必须 catch 错误,例如 NoSuchFieldException

然后我们想访问那个字段,但是我们想访问哪个对象的name?好吧,我们必须指定使用具体对象。我们需要f1f2,否则我们无法知道应该访问哪个(可能有很多)names。

通过执行nameField.get(f2),我们说我们要从对象f2 访问nameField 的关联字段(来自Fooname 字段)。但请注意 - 该字段可能无法访问(例如,它可能是 private),这就是为什么我们必须像 IllegalAccessException 这样的 catch 错误。


老实说,您甚至可以通过多做一些魔术来访问 private 字段,但这有点超出了问题的范围。因此,总而言之,您需要一个 T 类型的实际对象来访问其字段。 Class&lt;T&gt; 可以让你做一点meta-magic,但要获得具体的东西,你需要具体的对象。

当然,如果您没有具体的对象,而只有一个Class&lt;T&gt;,您可以使用fClass.getDeclaredConstructor().newInstance() 从它创建一个T

编辑:当然,您的 API 可能会使用 Class&lt;T&gt; 发挥自己的魔力。我不熟悉您使用的 API(PredicateSpecRoute.AsyncBuilder),所以我不能代表他们。

【讨论】:

    【解决方案2】:

    你不能只使用 Class&lt;T&gt; 参数来做到这一点,除非你愿意使用凌乱而脆弱的反射。

    但是,您可以添加一个表示用户属性的 get 方法的参数:

    private <T> Function<PredicateSpec, Route.AsyncBuilder> walletRoute(
            String server,
            String path,
            String method,
            Class<T> inClass,
            Function<T, String> getUserMethod) {
    
        return r -> r.path(path)
                     .and().method(method)
                     .and().readBody(inClass, requestBody ->
                         walletDiscoveryService(
                             getUserMethod.apply(requestBody)).equals(server))
                     .uri("http://" + server);
    }
    

    调用方法看起来像这样:

    walletRoute("localhost:3000", "/api/account/**", "POST",
        BankAccount.class, BankAccount::getUser);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      • 1970-01-01
      • 2018-09-30
      相关资源
      最近更新 更多