关于oop:Java – 如何动态创建“匿名”类

Java - How to create “anonymous” class on the fly

我可以做这个:

1
2
3
new Object(){
    public void hi(){}
}.hi();

我怎么做(或者我可以)这样做:

1
2
3
4
IWouldRatherStayAnonymous t = new IWouldRatherStayAnonymous extends Thread(){
    public void NoNoYouCantCallMe(String s){}
};
t.NoNoYouCantCallMe("some useful data that i will need later, but don't want to save this anonymous class");

如果我不能在语法上做到这一点,任何人都可以解释为什么这将永远不会工作的逻辑/实现设计?

==========================================

编辑:澄清 - 我想创建一个匿名类而不保存它,然后实例化该匿名类的实例。 我只需要1个引用,因为我不打算经常使用这个引用(即使在同一个外部类中也不想使用它)。 但是,我只想将一些数据传递给这个匿名类只有一次(理想情况下是构造函数,但你会注意到你实际上不能覆盖Thread()中的构造函数)。

这真的不是一个巨大的交易,我只是好奇,如果这是可行的,但似乎从一些答案,它是可行的,但不是很漂亮。


你的问题有点令人困惑,但听起来你想要一个扩展Thread并实现某个接口的类,但是在它所使用的方法之外无法访问它(这将排除私有嵌套类)。在这种情况下使用本地类:

1
2
3
4
5
6
7
void myMethod() {
    class Impl extends Thread implements SomeInterface {
        @Override
        public void someInterfaceMethod(String s){ }
    }
    new Impl().someInterfaceMethod("data");
}

编辑:为了响应您的更新,您可以自然地为本地类提供构造函数并传入数据,或者让它关闭外部方法中的final变量。


if i can't syntactically do this, can anyone explain the
logic/implementation design that of why this would never work?

我可以解释。如果Java中有表达式foo.bar(...),则编译器必须确保表达式foo的静态(编译时)类型支持方法bar()。在第一种情况下,左边的表达式是匿名类创建表达式,因此它的静态类型是具有该方法的匿名类。

但是,在第二种情况下,左侧的表达式是一个变量。变量的静态类型是声明变量的类型。在Java中,必须使用显式类型声明变量(没有varauto像其他语言一样)。因此,其静态类型必须是命名类型。 Thread并且没有其他命名类型支持方法NoNoYouCantCallMe();只有匿名类支持此方法,并且您不能声明匿名类类型的变量。这就是为什么它在语法上不可能。

However, I would like simply like to pass some data to this anonymous
class only once (ideally the constructor, but you will notice that you
can't actually override the constructor in Thread()).

这很简单。您不需要将数据显式传递到匿名类,因为在匿名类和本地类中,您可以自动访问周围范围内的final变量。因此,您只需直接使用外部数据,而不是传入数据。

1
2
3
4
5
final String someData ="blah";

Thread t = new Thread() {
    public void run() { doStuffWith(someData); }
};


您可以使用自由变量捕获将数据传递给匿名内部类,并将要捕获其值的任何变量声明为final

1
2
3
4
5
6
7
public void myFunction(final int magicNumber) {
  new Thread() {
    @Override public void run() {
      System.out.println("I can access the magic number:" + magicNumber);
    }
  }.start();
}


如果你真的想在不使用接口或抽象类的情况下做一些像这样的淫秽,你可以通过反射调用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void testCrazyStuff() throws IllegalArgumentException, SecurityException,
    IllegalAccessException, InvocationTargetException, NoSuchMethodException
{
    Object object = new Thread()
    {
        public void ICanBeCalled( String s )
        {
            System.out.println( s );
        }
    };
    object.getClass()
      .getMethod("ICanBeCalled", String.class )
      .invoke( object,"Whatever string!" );
}

但这是毫无意义的......采用一个简单的解决方案,比如其他建议的界面或抽象类更好。


你可以创建一个内部类,只是不要匿名。匿名意味着您没有类型。

我建议在同一个文件中使用命名的内部类或第二个类。我经常使用第二种,它不能公开。


有两种方法可以访问匿名类的方法:

  • 通过实现接口或扩展类,然后调用您在匿名类中实现的方法,或者
  • 通过使用反射(我假设你知道它是如何完成的,但不想这样做)。

Java没有提供静态捕获匿名类类型的方法,例如以类似于C#的var关键字的方式。

在您的情况下,您可以使用void NoNoYouCantCallMe(String)方法创建抽象类型,并使用该类作为匿名类的基础,如下所示:

1
2
3
4
5
6
7
8
9
abstract class MyThreadBase extends Thread {
    public abstract void YesICanCallYou(String s);
}
...
MyThreadBase t = new MyThreadBase() {
    public void YesICanCallYou(String s){}
};
...
t.YesICanCallYou();


一种选择是声明匿名类可以实现的单个抽象类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface IDontWorkForSomeReasonThread {
    public void NoNoYouCantCallMe(String s);
}

public static abstract class MyAbstractThread
extends Thread
implements IDontWorkForSomeReasonThread { }

public static void main (String[] args) throws java.lang.Exception {
    IDontWorkForSomeReasonThread t = new MyAbstractThread() {
      public void NoNoYouCantCallMe(String s){}
    };

    t.NoNoYouCantCallMe("foo" );
}