访问匿名类的构造函数

236

假设我有一个具体类Class1,并且我正在将其创建为匿名类。

Object a = new Class1(){
        void someNewMethod(){
        }
      };

现在是否有任何方法可以重载此匿名类的构造函数,就像下面所示:

Now is there any way I could overload the constructor of this anonymous class. Like shown below

Object a = new Class1(){
        void someNewMethod(){
        }
        public XXXXXXXX(int a){
          super();
          System.out.println(a);
        }
      };

使用某些内容在 xxxxxxxx 上命名构造函数?


如果父类有构造函数:https://dev59.com/GmIi5IYBdhLWcg3w8AFy - Vadzim
10个回答

307

根据Java语言规范第15.9.5.1节内容:

匿名类不能具有显式声明的构造函数。

抱歉 :(

编辑:作为替代方法,您可以创建一些final局部变量,并/或在匿名类中包含实例初始化程序。例如:

public class Test {
    public static void main(String[] args) throws Exception {
        final int fakeConstructorArg = 10;

        Object a = new Object() {
            {
                System.out.println("arg = " + fakeConstructorArg);
            }
        };
    }
}

虽然有些丑陋,但它可能会对你有所帮助。或者,可以使用嵌套类 :)


18
我相信Arne没有抄袭,他对Java的了解足以让他在抄袭时公平地给予出处。 - Johannes Schaub - litb
25
天啊,有人指责 Jon Skeet 抄袭了吗? - user
当一个方法被覆盖后,我该如何在println中调用Test的超类中的方法? - Mark Jeronimus
啊,我想覆盖超类构造函数...然后我明白了没有显式声明的构造函数意味着根本不能覆盖。我猜是这样。 - n611x007
变量必须是final的原因是什么? - banarun
显示剩余3条评论

110

那是不可能的,但您可以像这样添加一个匿名初始化器:

final int anInt = ...;
Object a = new Class1()
{
  {
    System.out.println(anInt);
  }

  void someNewMethod() {
  }
};

不要像我一样忘记在局部变量或匿名类使用的参数声明中加上final关键字,比如anInt。


这实际上非常类似于构造函数。我可以访问抽象基类的受保护成员。在实例化匿名类之前,其他所有操作都可以在代码中完成。 - Stefan Steinegger

87

这里有另外一个解决问题的方法:

public class Test{

    public static final void main(String...args){

        new Thread(){

            private String message = null;

            Thread initialise(String message){

                this.message = message;
                return this;
            }

            public void run(){
                System.out.println(message);
            }
        }.initialise(args[0]).start();
    }
}

22
不错的解决方案,但这里使用了Thread,刚开始有些误导(有一瞬间我以为你创建了一个单独的线程来初始化东西!) - scorpiodawg
8
请注意,在定义了变量t后,如果该函数未在类/接口类型中定义,则不能调用t.initialise()函数。 - Aram Kocharyan
1
这使得它更像一个构造函数。 - v010dya
1
我喜欢这个解决方案!它清楚地表明initialise()方法是在Thread构造函数之后调用的。然而,对于实例初始化程序,至少对我来说,并不明显这总是得到保证。 - muelleth

22

我知道这个帖子已经太旧了,不能再回答了。但我仍然认为它是值得的。

虽然你不能有一个显式的构造函数,但如果你的意图是调用超类的一个(可能是受保护的)构造函数,那么下面的内容就是你要做的全部。

StoredProcedure sp = new StoredProcedure(datasource, spName) {
    {// init code if there are any}
};

这是一个在Spring中创建StoredProcedure对象的示例,通过传递DataSourceString对象实现。

所以,关键是,如果您想创建一个匿名类并想要调用超类构造函数,则创建匿名类时签名与超类构造函数匹配


3

是的,你不能在匿名类中定义构造函数,但并不意味着匿名类没有构造函数。有点困惑...... 实际上,在匿名类中你不能定义构造函数,但编译器会生成一个与其父类构造函数参数一致的构造函数。如果父类有多个构造函数,则匿名类只会有一个构造函数。


3
您可以在抽象类中添加一个接受初始化参数的构造函数。Java规范只规定了匿名类是由(可选)的抽象类或接口实现派生而来,她本身不能有构造函数。
以下是完全合法并且可行的代码示例:
static abstract class Q{
    int z;
    Q(int z){ this.z=z;}
    void h(){
        Q me = new Q(1) {
        };
    }
}

如果您有可能自己编写抽象类,请在其中添加这样一个构造函数,并在没有更好的解决方案时使用流畅的API。您可以通过这种方式重写原始类的构造函数,创建一个带参数的命名兄弟类并使用它来实例化匿名类。

(匿名类的存在意义)如何将该代码放在一个函数中? - Pacerier

2
如果您不需要传递参数,则初始化代码就足够了,但如果您需要从构造函数传递参数,则有一种方法可以解决大多数情况:
Boolean var= new anonymousClass(){
    private String myVar; //String for example

    @Overriden public Boolean method(int i){
          //use myVar and i
    }
    public String setVar(String var){myVar=var; return this;} //Returns self instane
}.setVar("Hello").method(3);

如果我理解你的代码正确,anonymousClass 应该继承自 String(setVar 是 String 类型并返回 this),但是 String 是不可扩展的。我猜测 setVar 应该返回 anonymousClass 继承的类型。 - alfoks

2

Peter Norvig的《Java IAQ:不经常回答的问题》

http://norvig.com/java-iaq.html#constructors - 匿名类构造器

http://norvig.com/java-iaq.html#init - 构造器和初始化

总之,您可以构建像这样的东西...

public class ResultsBuilder {
    Set<Result> errors;
    Set<Result> warnings;

...

    public Results<E> build() {
        return new Results<E>() {
            private Result[] errorsView;
            private Result[] warningsView;
            {
                errorsView = ResultsBuilder.this.getErrors();
                warningsView = ResultsBuilder.this.getWarnings();
            }

            public Result[] getErrors() {
                return errorsView;
            }

            public Result[] getWarnings() {
                return warningsView;
            }
        };
    }

    public Result[] getErrors() {
        return !isEmpty(this.errors) ? errors.toArray(new Result[0]) : null;
    }

    public Result[] getWarnings() {
        return !isEmpty(this.warnings) ? warnings.toArray(new Result[0]) : null;
    }
}

我不认识Peter Norvig,他是Google的科学家之一,这可能是他早期的作品之一,关于Java 1.1!从历史的角度来看很有趣 :) - pdem

1
在我的情况下,一个带有自定义构造函数的本地类作为匿名类起作用:
Object a = getClass1(x);

public Class1 getClass1(int x) {
  class Class2 implements Class1 {
    void someNewMethod(){
    }
    public Class2(int a){
      super();
      System.out.println(a);
    }
  }
  Class1 c = new Class2(x);
  return c;
}

1

在匿名类中拥有一个命名的重载构造函数是没有任何意义的,因为无论如何都没有办法调用它。

根据您实际尝试做什么,只需访问在类外部声明的最终局部变量,或者像Arne所示使用实例初始化程序可能是最佳解决方案。


如果需要,该语言可以轻松地将“普通”构造函数参数转换为匿名类的参数。但是构造函数声明的语法可能看起来相当奇怪... - Jon Skeet
1
它不能只是说像基类构造函数一样声明构造函数吗?我认为这样做没有问题。 - Johannes Schaub - litb

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