【问题标题】:Get subclass back when stored as superclass存储为超类时取回子类
【发布时间】:2020-11-14 09:59:13
【问题描述】:

所以我已经尝试修复这个问题几个小时了,但无济于事。

基本上,我有 A 类和 B 类扩展 AA 有一个字符串字段“name”,另一个字段“id”,类B 在实例化时更改name。然后B 被存储为A,唯一将A 类标识为B 类的是“id”字段。

我需要一种方法来从 A 类中获取 B 类所具有的更改后的“名称”。

这是一个代码示例:

class A {
    public String name = "test";
    public String id = "A_ID";
    
    public A(String id) {
        if (id.equals("B_ID") {
            // how would I get the name field of the B class, without instantiating B
        }
    }
}
class B extends A {
    {
        name = "test1";
        id = "B_ID";
    }
}

这是我尝试过的:

class A {
    public static Map<String, Class<? extends A>> REGISTRY = new HashMap<>();
    
    public String name = "test";
    public String id = "A_ID"; // is B_ID when changed
    
    public Object getField(String key) {
        Field field = REGISTRY.get(this.id).getSuperclass().getDeclaredField(key);
        return field.get(this); // <- the issue
    }
}
class B extends A {
    static {
        REGISTRY.put("B_ID", B.class);
    }
    public B() {
        name = "test1";
        id = "B_ID";
    }

}

这是我的解决方案的问题,将this 传递给field.get(this) 返回当前A 对象的字段,这是我们已经拥有的。我需要B 在实例化时更改的字段,但是当B 被实例化时,它会调用getField 几次,这会导致永久循环,我需要一种方法来从B 获取字段而不实例化@ 987654340@.

我尝试将 A 转换为 B 但这会导致 ClassCastException。

抱歉,这令人困惑,我无法发送整个类,因为它实际上是 1k+ 行,但基本上 ID 字段是与 A 一起存储的唯一数据,但仍来自 B,我需要一种方法来获取来自 B 的字段B 没有实例化它

编辑:我认为我的示例令人困惑,这是 pkm 的答案仍然存在问题的示例。

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

class A {
    public static Map<String, Class<? extends A>> REGISTRY = new HashMap<>();

    public String name = "test";
    public String id; // is B_ID when changed

    public A(String id) {
        this.id = id;
        this.getField("name") // should output test1
    }

    public Object getField(String key) throws NoSuchFieldException, SecurityException,
            IllegalArgumentException, IllegalAccessException {
        Field field = REGISTRY.get(this.id).getSuperclass().getDeclaredField(key);
        return field.get(this); // <- the issue, still returns "test"
    }
}


class B extends A {
    static {
        REGISTRY.put("B_ID", B.class);
    }

    public B() {
        super("B_ID");
        name = "test1";
    }
}

public class main {

    public static void main(String[] args) throws NoSuchFieldException, SecurityException,
            IllegalArgumentException, IllegalAccessException {
        new B(); // To update the registry
        A a = new A("B_ID");
        System.out.println(a.id);                //--> output: B_ID
        System.out.println(a.getField("name"));      //--> output: test, should be test1
    }

}

【问题讨论】:

  • 检查`if (this instanceof B)`不够吗?
  • 就像我说的那样,它都存储为 A。没有什么是 B 的实例,所以向下转换为 B 会导致 ClassCastException
  • “没有什么是 B 的实例”——当然,您通过 new B() 创建的实例除外。由于您发布的代码更改了该 B 实例的字段,但您没有保留对该对象的引用,因此无法使用该代码检索该值。不清楚你为什么在这里使用反射,也许只是为了隐藏试图获得你已经放弃的值的基本逻辑错误。但是由于所有实例都在构造函数中获取该值,因此您可以使用REGISTRY.get("B_ID").getConstructor().newInstance().name 获取该值,但仍然过度设计。
  • 您不知何故忘记提及您的问题与 JSON 序列化有关。除此之外,还有什么问题?该字段在A 中声明,因此在反序列化A 实例后,它仍将包含在序列化之前存储在原始B 实例中的。当然,由于您的示例代码不包含任何序列化/反序列化,因此示例代码中不会发生这种情况。在您的示例中,没有实例,只有 和构造函数提供的常量的注册表,根本不需要持久性。

标签: java class reflection


【解决方案1】:

通过将注册表从 Class&lt;? extends A&gt; 更改为 A,然后注册 B 的新实例来解决此问题。我认为这可能会导致未来出现问题,我一直在避免这样做,但它现在有效。

【讨论】:

    【解决方案2】:

    以下是更正后的代码:

    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    
    class A {
      public static Map<String, Class<? extends A>> REGISTRY = new HashMap<>();
    
      public String name = "test";
      public String id = "A_ID"; // is B_ID when changed
    
      public Object getField(String key) throws NoSuchFieldException, SecurityException,
          IllegalArgumentException, IllegalAccessException {
        Field field = REGISTRY.get(this.id).getSuperclass().getDeclaredField(key);
        return field.get(this); // <- the issue
      }
    }
    
    
    class B extends A {
      static {
        REGISTRY.put("B_ID", B.class);
      }
    
      public B() {
        name = "test1";
        id = "B_ID";
      }
    
    }
    
    
    public class Test {
      public static void main(String[] args) throws NoSuchFieldException, SecurityException,
          IllegalArgumentException, IllegalAccessException {
        A a = new B();
        System.out.println(a.name);                //--> output: test1
        System.out.println(a.getField("id"));      //--> output: B_ID
      }
    }
    
    

    根据您的说法:I need a way to get the changed "name" that the B class has, from the A class. 正确实现了,B_ID。

    【讨论】:

    • 我的例子解释得不好,我很抱歉。我已经更新了我的问题,以反映您的回答仍然存在问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-09
    • 2018-12-28
    • 2013-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多