如何解压/简化匿名内部类

Posted

技术标签:

【中文标题】如何解压/简化匿名内部类【英文标题】:How to Unpack/ Simplify Anonymous Inner Class 【发布时间】:2021-10-16 15:32:09 【问题描述】:

这篇文章的目的是找出如何避免使用匿名内部类。

我没有与inner anonymous classes 进行广泛的合作,但我正在尝试简化某人的包examples.introduction.novice.simpler.model; 中的以下代码行

public class Person 
    public final String name;

// --- Attribute
    public static final Attribute<Person, String> NAME = new SimpleAttribute<Person, String>("name") 
        public String getValue(Person person, QueryOptions queryOptions)  return person.name; 
    ;


所以为了打开上面的内容并使其更易于阅读,我重写了它(如果我错了,请纠正我):

public class Person 
    public final String name;
    
    public Person(String name) 
        this.name = name;
    
    
    
    // TODO: Simplify
    // ----------- Attributes -------------
    public static final Attribute<Person, String> NAME = new Name<Person, String>("name") ;


当然,我创建了Name 类:

public class Name<O, A> extends SimpleAttribute<O, A> 
    public Name(String attributeName) 
        super(attributeName);
        // TODO Auto-generated constructor stub
    

    public String getValue(Person person, QueryOptions queryOptions)  return person.name; 

    @Override
    public A getValue(O arg0, QueryOptions arg1) 
        // TODO Auto-generated method stub
        return null;
    



但是,我收到一个错误:

Exception in thread "main" java.lang.ExceptionInInitializerError
    at examples.introduction.novice.simpler.model.Introduction.main(Introduction.java:17)
Caused by: java.lang.IllegalStateException: Attribute 'name' (class examples.introduction.novice.simpler.model.Name) is invalid, cannot read generic type information from it. Attributes should typically EITHER be declared in code with generic type information as a (possibly anonymous) subclass of one of the provided attribute types, OR you can use a constructor of the attribute which allows the types to be specified manually.
    at com.googlecode.cqengine.attribute.support.AbstractAttribute.readGenericObjectType(AbstractAttribute.java:139)
    at com.googlecode.cqengine.attribute.support.AbstractAttribute.<init>(AbstractAttribute.java:43)
    at com.googlecode.cqengine.attribute.SimpleAttribute.<init>(SimpleAttribute.java:55)
    at examples.introduction.novice.simpler.model.Name.<init>(Name.java:11)
    at examples.introduction.novice.simpler.model.Person.<clinit>(Person.java:23)
    ... 1 more

回顾一下: 我试图在以下包中简化以下示例(这是我试图简化的原始工作示例,它有两个类:名为Introduction.java 的主类和名为Person.java 的伴随类): https://github.com/mrarthurwhite/CQEngineIntroExample/tree/master/src/examples/introduction/novice

在下面的包中尝试简化它(它只是尝试解包Person.java 类: https://github.com/mrarthurwhite/CQEngineIntroExample/tree/master/src/examples/introduction/novice/simpler/model

正如善良和耐心的读者可能从上面观察到的那样,我正在尝试解压缩 Attribute 的匿名内部类。这是Attribute 类和cqengine 库的链接: https://github.com/npgall/cqengine/blob/master/code/src/main/java/com/googlecode/cqengine/attribute/Attribute.java

以下是子类SimpleAttribute 的链接,它是Attribute 的后代: https://github.com/npgall/cqengine/blob/master/code/src/main/java/com/googlecode/cqengine/attribute/SimpleAttribute.java

【问题讨论】:

如果它以前有效并且已经很容易阅读,为什么需要更改它(并可能在过程中破坏它)? @QBrute 我正在尝试解压缩匿名内部类,因为有些程序员对它不是很熟悉,包括我自己。如您所见,我不太了解匿名内部类,当我尝试对其进行解压缩时,出现上述错误。 【参考方案1】:
public class Name extends SimpleAttribute<Person, String> 
    public Name() 
        super("name");
        // TODO Auto-generated constructor stub
    

    @Override
    public String getValue(Person person, QueryOptions queryOptions)  
        return person.name; 
    

试试这个。 如果您进行新的通用实现,则基本上必须使用反射来获取值。如果你这样做,它会更复杂,而不是更简单。

【讨论】:

谢谢!这就是我要找的。一种避免编写匿名内部类的方法。当我使用您对 Name 类的定义时,我看到 Person.java 给出了一个编译错误 The type Name is not generic; it cannot be parameterized with arguments &lt;Person, String&gt; 您能否对这个问题投赞成票,有人恶意投反对票,这可能会导致其他响应者没有注意到它。 你需要使名称对象像 new Name() @Arthur W 另一个答案是错误的,因为构造中不应该有参数。命名参数永远不会是“名称”以外的另一个属性。将我的标记为答案:)【参考方案2】:

对于您的代码,Java 编译器会生成一个更具体的非泛型具体类,如下所示(当然名称会有所不同):

public class Name extends SimpleAttribute<Person, String> 
    public Name(String attributeName) 
        super(attributeName);
    

    @Override
    public String getValue(Person person, QueryOptions queryOptions)  return person.name; 


注意超类类型是SimpleAttribute&lt;Person, String&gt;SimpleAttribute 的类型参数都是具体类型,而不是像您尝试的类型变量 OA

你会这样使用它:

public static final Attribute<Person, String> NAME = new Name("name");

从异常信息来看,

属性“名称”(类 examples.introduction.novice.simpler.model.Name)无效,无法从中读取泛型类型信息。

您正在使用的库依赖于这种行为(类型参数是具体类型的事实)来读取类型参数。它可能会调用以下内容:

((ParameterizedType)NAME.getClass().getGenericSuperclass()).getActualTypeArguments()

如果SimpleAttribute 的类型参数是OA,就像您尝试解压缩匿名类一样,返回的数组将没有任何实际类型,这可能就是它说“无法读取泛型”的原因类型信息”。

另一方面,如果您使用匿名类(或答案开头的代码),则生成的内部类将具有SimpleAttribute&lt;Person, String&gt; 作为超类,并且库将能够读取有用的信息来自它。

还要注意,即使库没有碰巧读取泛型类型参数,您对匿名类的解包仍然不会与原始代码执行相同的操作。即,您覆盖抽象方法的方式:

@Override
public A getValue(O arg0, QueryOptions arg1) 
    // TODO Auto-generated method stub
    return null;

与原始代码中抽象方法的覆盖方式不同:

public String getValue(Person person, QueryOptions queryOptions)  return person.name; 

您在Name 中定义的getValue 的另一个重载并不真正相关。

【讨论】:

覆盖的 getvalue 方法是由 Eclipse 生成的,因为没有它,该类会出现编译器错误:The type Name&lt;O,A&gt; must implement the inherited abstract method SimpleAttribute&lt;O,A&gt;.getValue(O, QueryOptions) 我试图避免使用内部匿名类 - 有没有办法做到这一点? @ArthurW 没错,此时您应该意识到您的“拆包”有问题。有助于实现抽象类的重载是返回 null 的重载。另一个不相关。看到你的错误了吗?如果Name 是通用的,它必须有一个getValue 方法,它接受一个O 和一个QueryOptions,但这永远不会起作用,因为你想返回一个人的 名称,所以@ 987654343@ 不应该是通用的。有关将匿名类正确转换为命名类的信息,请参见答案的开头。什么不清楚? 尚不清楚如何完全避免使用匿名内部类?什么实现可以完全避免这种情况?抱歉,但由于某种原因,我的认知能力在这个时候并不是最佳的(出于某种奇怪的原因)。 @Sweeper @ArthurW 你看到我回答的latest edit了吗?我添加了一个示例,说明如何重写 NAME 的声明以使用 namedName 您的建议成功了!谢谢!惊人的!我不知道如何或为什么,在我的想法不那么模糊之后,我会复习你的答案......

以上是关于如何解压/简化匿名内部类的主要内容,如果未能解决你的问题,请参考以下文章

Lambda表达式简化匿名内部类

Lambda表达式简化匿名内部类

匿名内部类创建线程,简化线程创建代码

匿名内部类

关于接口配合匿名内部类使用的问题讲究

为什么匿名内部类可以实例化并实现抽象方法?lambda表达式是简化了匿名内部类的实现过程吗?