在实例化类的同时声明函数

3
我看到了以下Java代码,但不确定它的含义。我们可以在实例化类后写'{'中的代码吗?例如:new TestClass { */ code goes here */ }

但是当我尝试运行代码时,输出中没有看到“Z is 10”。请问有什么相关Java特性的链接可以提供更多信息?
class TestClass {
    int z;
    public TestClass(){
    z=10;
    }
    public int getZ(){
    return z;
    }
    public void setZ(int z){
    this.z=z;
    }
}

class A
{   
    public static void main (String[] args) throws java.lang.Exception
    {
    TestClass TC = new TestClass() {
            public void testprint() {
        System.out.println("Z is " + getZ());
        }
     };
    }
}
5个回答

8

这是一个匿名内部类。它创建了一个对象,该对象是某个子类 TestClass 的实例,您不想给该子类命名(因为您只需要在运行时创建对象,而且您不需要在其他地方使用该类)。

代码仅初始化对象,您实际上没有调用正在创建的对象上的方法,所以您看不到任何输出。您可以添加一行代码

public static void main (String[] args) throws java.lang.Exception
{
    TestClass TC = new TestClass() {
        public void testprint() {
            System.out.println("Z is " + getZ());
        }
    };
    // <- call the new method (>_< this won't compile)
    TC.testprint(); 
}

但这并不起作用,因为testprint在TestClass中没有被定义为方法,本地变量引用对象的类型为TestClass,因此编译器期望在TestClass或其超类上找到定义的方法。您可以向TestClass添加某种方法,例如:

abstract class TestClass {
    int z;
    public TestClass(){
        z=10;
    }
    public int getZ(){
        return z;
    }
    public void setZ(int z){
        this.z=z;
    }
    // add new method
    public abstract void testprint();
}

现在编译器知道TestClass类型的对象将有一个名为testprint的方法。我不必使TestClass成为抽象类,我可以向其中添加testprint的实现。然而,由于TestClass是抽象的,因此很明显新的对象不是TestClass而是它的某个子类。
或者,通过 TestClass 的另一个已公开方法来调用此方法。保持 TestClass 不变,但将主要方法更改为:
public static void main (String[] args) throws java.lang.Exception
{
    TestClass TC = new TestClass() {
        public void testprint() {
            System.out.println("Z is " + super.getZ());
        }
        // override Z getter to call testprint
        @Override public int getZ(){ 
            testprint();
            return z;
        }
    };
    TC.getZ(); // <- call method on object created above
}

testprint必须更改为调用超类版本的Z获取器,以便我们不会有无限递归。现在匿名子类上的Z获取器调用了testprint方法,因此您可以调用获取器并查看输出:

Z is 10

这段代码没有为子类分配名称,但它仍然会被分配一个名称。如果我在main函数的末尾添加一行代码来查看匿名类的内部名称:

    System.out.println(TC.getClass());

它打印出来了

class A$1

实际上,无法直接调用定义在匿名类中的新方法并不是限制。您需要在某些情况下提供一个匿名类以将该类的实现传递到其他对象的某个方法中,接收匿名类对象的东西无法(也不应该)知道如何调用新方法,参见LSP


1
问题在于,如果testprint()不是TestClass的方法,而只是在匿名类中定义(->不覆盖方法),则无法像这样调用它... - Puce
作为替代方案,如果基类也没有定义该方法,则可以从匿名类的另一个方法或初始化程序中调用该方法。 - Puce
谢谢Nathan,所以基本上它是一个子类,但在这种情况下,匿名类不能声明一个未在超类中声明的新方法。我是对的吗? - user2588495
@user2588495:它可以声明任何想要的东西,这很好。问题在于如何调用该方法。您创建它是为了让其他东西调用它,调用它的东西需要以某种方式了解该方法(除非它仅在内部使用)。我的示例使TestClass抽象显示了使调用方了解该方法,覆盖Z getter的示例显示了在内部使用该方法。 - Nathan Hughes

2

这是匿名内部类的示例。它就像一个扩展了TestClass的类。因此,您没有调用该类的任何方法,因此不会打印任何内容。


1
在你的代码中
TestClass TC = new TestClass() {
            public void testprint() {
        System.out.println("Z is " + getZ());
        }
     };

这被称为匿名内部类,因此仅会进行初始化。您可以在上面的代码片段后使用TC.testprint();
注意 - TC是可以的,但根据Java命名约定规则,请使用简单字母。 您可以从这里阅读更多信息 - http://www.iwombat.com/standards/JavaStyleGuide.html

请注意,您建议调用TC.testprint是不正确的,因为testprint不是TestClass上的一个方法。(我最初也犯了同样的错误。) - Nathan Hughes
非常好的观点,我刚看到了。非常感谢。我看到你编辑了答案,所以我就不改了。 - Lasitha Benaragama

1

在这样做时,您隐式地继承了TestClass并立即实例化它,添加了一个方法testPrint()

这被称为匿名类


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接