我遇到了有趣的事情(在Java和C#中都一样)。 Java代码:
public class TestStuff {
public static void main(String[] args) {
Printer p = new PrinterImpl();
p.genericPrint(new B());
}
}
class PrinterImpl implements Printer {
void print(A a) {
System.out.println("a");
}
void print(B b) {
System.out.println("b");
}
@Override
public <T extends A> void genericPrint(T b) {
print(b);
}
}
interface Printer {
public <T extends A> void genericPrint(T a);
}
class A {
}
class B extends A{
}
C#代码:
namespace TestStuff
{
internal class Program
{
private static void Main(string[] args)
{
var printer = new Printer();
printer.GenericPrint(new B());
}
}
public class Printer
{
public void Print(A a)
{
Console.WriteLine("a");
}
public void Print(B b)
{
Console.WriteLine("b");
}
public void GenericPrint<T>(T a) where T : A
{
Print(a);
}
}
public class B : A
{
}
public class A
{
}
}
当我写这样的东西时,我希望在这两种情况下都能看到“b”。 但是,正如你所看到的,它是“一个”印刷的东西。
我已阅读C#语言规范,并表示在编译时选择了重载方法。它解释了为什么它以这种方式工作。
但是,我没时间用Java语言规范来检查它。
有人可以更详细地解释发生了什么以及为什么?我怎么能实现我想要的呢?
提前致谢!
答案 0 :(得分:5)
关键是要理解泛型仅在java的编译时可用。它只是编译器在编译时使用的语法糖,但在生成类文件时会丢弃。
因此,代码:
public <T extends A> void genericPrint(T b) {
print(b);
}
编译成:
public void genericPrint(A b) {
print(b);
}
由于print
的参数是A类型,因此重载版本print(A a)
是已解析的版本。我建议对A或Visitor pattern的实例使用多态调用来回调用于您的用例的PrinterImpl。
类似的东西:
interface Visitor {
void visit(A a);
void visit(B b);
}
class PrinterImpl implements Printer, Visitor {
void print(A a) {
System.out.println("a");
}
void print(B b) {
System.out.println("b");
}
public <T extends A> void genericPrint(T b) {
b.accept(this);
}
public void visit(A a) {
print(a);
}
public void visit(B b) {
print(b);
}
}
interface Printer {
public <T extends A> void genericPrint(T a);
}
class A {
public void accept(Visitor v) {
v.visit(this);
}
}
class B extends A {
public void accept(Visitor v) {
v.visit(this);
}
}
答案 1 :(得分:1)
确实在编译时选择了重载方法,对于java也是如此(动态方法调度)。 然而,泛型的工作方式略有不同。您的方法GenericPrinter只能使用A类或其派生类。它是对该方法的约束。假设您在GenricPrinter类中调用了A中定义的方法。
public class A
{
void DoSomethingA()
{
}
}
.
.
.
public void GenericPrint<T>(T a) where T : A
{
//constraint makes sure this is always valid
a.DoSomethingA();
Print(a);
}
因此,此约束将确保仅允许包含上述方法的A或其子类。 虽然传入A的子类的实例但由于constaint,GenericPrinter会将子类视为A. 只需删除约束部分(T:A),就可以按预期打印B.
答案 2 :(得分:0)
没有运行时检查a
方法中的GenericPrint
类型。您使用where T : A
部分强制执行的唯一事项是,您可以致电Print
。
顺便说一下,除了那个通用方法:如果你想要打印a
,虽然它是B
的一个实例,那么你必须将该变量声明为A obj = new B()
。< / p>