覆盖 Java 中的抽象泛型方法 [英] Overriding abstract generic method in Java

查看:37
本文介绍了覆盖 Java 中的抽象泛型方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题大纲

我正在对当前项目基础的更好部分进行泛化,并且我有一个想法,我决定测试关于覆盖抽象方法的内容.这是我在 Java 中的测试类:

公共抽象类 Base {public abstract <T extends Base>测试();}

第一次实现:

public class Inheritor extends Base {@覆盖公共继承者测试(){返回空;}}

第二次实施:

public class Inheritor2 extends Base {@覆盖public <T extends Base>测试(){返回空;}}

问题 1

为什么要编译?我承认我对它是合法的寄予厚望,因为它使合同不仅确保它返回扩展 Base 的东西,而且已经更加专业化(这样我就不需要稍后将结果投射到我的专业类).

听起来不错,但我真的履行了基类强迫我遵守的契约吗?我在 Inheritor 中的重写实现失去了一定的通用性,不是吗?我在 Inheritor 中实现的这个方法永远不会返回 Inheritor2 的实例,抽象方法似乎强制执行了这种可能性(因为两者都扩展了 Base代码>).

我想指出一些文档摘录.我的猜测是它与类型擦除有关,如果有人在他/她的回答中提到它的准确性会很好.

问题 2

除了我在标题中提到的名称之外,这个程序还有其他正式名称吗?

问题 3

这在 C# 中可行吗?同事的scratch test好像编译失败了.那么通用抽象方法覆盖的方法是否有所不同?

解决方案

这里是技术细节.

关于覆盖:

<块引用>

在类C中声明或继承的实例方法mC,覆盖来自 CA 中声明的另一个方法 mA,如果满足以下所有条件是真的:

  • AC 的超类.
  • C 不继承 mA.
  • mC 的签名是 mA 签名的子签名(第 8.4.2 节).
  • 以下情况之一为真:
    • mApublic.
    • [...]

在你的情况下,ABaseCInheritorBase#test()mAInheritor#test()mC.

mCmA 因为

<块引用>

方法m1的签名是a的签名的子签名方法 m2,如果:- m2 与 m1 具有相同的签名,或- m1 的签名与 m2 的签名的擦除(第 4.6 节)相同.

mA的擦除是

public abstract Base test()

mC

public 继承者 test()

是一个子签名.返回类型呢?

<块引用>

如果返回类型为 R1 的方法声明 d1 覆盖或隐藏了声明另一个返回类型为 R2 的方法 d2,则 d1 必须是返回类型替代(§8.4.5) 对于 d2,或编译时错误发生.

按照return-type-substitutable,我们看到

<块引用>

如果 R1 是引用类型,则以下情况之一为真:

  • R1 可以通过未经检查的转换(第 5.1.9 节)转换为 R2 的子类型.

InheritorT extends Base 的一个子类型,通过未经检查的转换,所以我们都很好(尽管你应该从编译器那里得到警告).>

所以回答你的问题:

  1. 它根据 Java 语言规范中声明的规则进行编译.
  2. 这叫做覆盖.
  3. 我没有完整的答案,但 C# 似乎没有类型擦除,因此这些规则不适用.

<小时>

未经检查的转换的危险会让您这样做

class Inheritor extends Base {@覆盖公共继承者测试(){返回新的继承者();}}

然后

Base ref = new Inheritor();Inheritor2 错误 = ref.test();

这会在运行时导致 ClassCastException.使用风险自负.

Problem outline

I'm generifying the better part of my current project's base and I had an idea that I decided to test regarding overriding an abstract method. Here are my test classes in Java:

public abstract class Base {

    public abstract <T extends Base> T test();

}

First implementation:

public class Inheritor extends Base {

    @Override
    public Inheritor test() {
        return null;
    }

}

Second implementation:

public class Inheritor2 extends Base {

    @Override
    public <T extends Base> T test() {
        return null;
    }

}

Question 1

Why does it compile? I admit I had high hopes it would be legal, since it makes the contract not only ensure it returns something that does extend Base, but is more specialized already (so that I don't need to cast the result to my specialized class somewhere later).

All sounds nice but do I really fulfill the contract that the base class forces me into? My overriden implementation in Inheritor loses certain layer of genericness doesn't it? My implementation of this method in Inheritor doesn't ever return an instance of Inheritor2, possibility of which the abstract method seemed to enforce (as both extend Base).

I would like pointing to some excerpt from documentation. My guess is it has something to do with type erasure, would be nice if someone mentioned it's accuracy in his/her answer.

Question 2

Does this procedure have a formal name other than one I stated in the title?

Question 3

Is this possible in C#? Colleague's scratch test seemed to fail on compilation. Is there then a difference in approach to generic abstract method overriding?

解决方案

Here are the technicalities.

Concerning overriding:

An instance method mC declared in or inherited by class C, overrides from C another method mA declared in class A, iff all of the following are true:

  • A is a superclass of C.
  • C does not inherit mA.
  • The signature of mC is a subsignature (§8.4.2) of the signature of mA.
  • One of the following is true:
    • mA is public.
    • [...]

In your case, A is Base and C is Inheritor, Base#test() is mA and Inheritor#test() is mC.

mC is a subsignature of mA because

The signature of a method m1 is a subsignature of the signature of a method m2 if either: - m2 has the same signature as m1, or - the signature of m1 is the same as the erasure (§4.6) of the signature of m2.

The erasure of mA is

public abstract Base test()

and mC

public Inheritor test()

is a subsignature. What about the return type?

If a method declaration d1 with return type R1 overrides or hides the declaration of another method d2 with return type R2, then d1 must be return-type-substitutable (§8.4.5) for d2, or a compile-time error occurs.

Following the return-type-substitutable, we see

If R1 is a reference type then one of the following is true:

  • R1 can be converted to a subtype of R2 by unchecked conversion (§5.1.9).

Inheritor is a subtype of T extends Base through unchecked conversion, so we're all good (though you should have gotten a warning from your compiler).

So to answer your questions:

  1. It compiles because of the rules declared in the Java Language Specification.
  2. It's called overriding.
  3. I don't have a full answer for you, but C# doesn't seem to have type erasure, so these rules wouldn't apply.


The dangers of unchecked conversion would allow you to do

class Inheritor extends Base {
    @Override
    public Inheritor test() {
        return new Inheritor();
    }
}

and then

Base ref = new Inheritor();
Inheritor2 wrong = ref.<Inheritor2>test();

which would cause a ClassCastException at runtime. Use it at your own risk.

这篇关于覆盖 Java 中的抽象泛型方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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