如何将参数传递给匿名类?

Posted

技术标签:

【中文标题】如何将参数传递给匿名类?【英文标题】:How to pass parameters to anonymous class? 【发布时间】:2011-07-03 16:35:14 【问题描述】:

是否可以向匿名类传递参数或访问外部参数?例如:

int myVariable = 1;

myButton.addActionListener(new ActionListener() 
    public void actionPerformed(ActionEvent e) 
        // How would one access myVariable here?
    
);

监听器有什么方法可以访问 myVariable 或传递 myVariable 而不将监听器创建为实际的命名类?

【问题讨论】:

可以从封闭方法中引用final局部变量。 我很喜欢 Adam Mmlodzinski 的建议,即定义一个初始化私有 myVariable 实例的私有方法,并且由于返回 this 而可以在右大括号处调用。 这个问题有一些共同的目标:***.com/questions/362424/… 您也可以在匿名类内部使用全局类变量。也许不是很干净,但它可以完成这项工作。 【参考方案1】:

是的,通过添加一个返回“this”的初始化方法,并立即调用该方法:

int myVariable = 1;

myButton.addActionListener(new ActionListener() 
    private int anonVar;
    public void actionPerformed(ActionEvent e) 
        // How would one access myVariable here?
        // It's now here:
        System.out.println("Initialized with value: " + anonVar);
    
    private ActionListener init(int var)
        anonVar = var;
        return this;
    
.init(myVariable)  );

不需要“最终”声明。

【讨论】:

哇...太棒了!我已经厌倦了创建一个final 引用对象,这样我就可以在我的匿名类中获取信息。谢谢分享! 为什么init()函数必须返回this?我真的不明白语法。 因为您的 myButton.addActionListener(...) 需要一个 ActionListener 对象作为您调用其方法时返回的对象。 我猜.. 虽然我觉得这很丑,但它确实有效。我发现大多数时候我可以简单地将必要的变量和函数参数设为 final 并直接从内部类中引用它们,因为通常它们只是被读取。 更简单:private int anonVar = myVariable;【参考方案2】:

从技术上讲,不,因为匿名类不能有构造函数。

但是,类可以从包含范围内引用变量。对于匿名类,这些变量可以是包含类的实例变量,也可以是标记为 final 的局部变量。

编辑:正如彼得所指出的,您也可以将参数传递给匿名类的超类的构造函数。

【讨论】:

匿名类使用其父类的构造函数。例如new ArrayList(10) 好点。所以这是将参数传递给匿名类的另一种方法,尽管您可能无法控制该参数。 匿名类不需要构造函数 匿名类可以有实例初始化器,它可以作为匿名类中的无参数构造函数。这些以与字段分配相同的顺序执行,即在 super() 之后和实际构造函数的其余部分之前。 new someclass() fields; initializer fields; methods() 。它有点像静态初始化器,但没有 static 关键字。 docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.6 看这个***.com/a/3045185/1737819它说如何在没有构造函数的情况下实现。【参考方案3】:

是的。您可以捕获对内部类可见的变量。唯一的限制是它必须是final

【讨论】:

从匿名类引用的实例变量不必是 final afaik。 实例变量通过this 引用,这是最终的。 如果我不想将变量更改为final 怎么办?我找不到任何替代方案。这可能会影响设计为final 的原点参数。【参考方案4】:

像这样:

final int myVariable = 1;

myButton.addActionListener(new ActionListener() 
    public void actionPerformed(ActionEvent e) 
        // Now you can access it alright.
    
);

【讨论】:

【参考方案5】:

这会变魔术

int myVariable = 1;

myButton.addActionListener(new ActionListener() 

    int myVariable;

    public void actionPerformed(ActionEvent e) 
        // myVariable ...
    

    public ActionListener setParams(int myVariable) 

        this.myVariable = myVariable;

        return this;
    
.setParams(myVariable));

【讨论】:

【参考方案6】:

如http://www.coderanch.com/t/567294/java/java/declare-constructor-anonymous-class 所示,您可以添加实例初始化程序。这是一个没有名称的块,首先执行(就像构造函数一样)。

看起来Why java Instance initializers? 也讨论了它们,How is an instance initializer different from a constructor? 讨论了与构造函数的区别。

【讨论】:

这并不能解决所提出的问题。您仍然会遇到访问局部变量的问题,因此您需要使用 Adam Mlodzinski 或 adarshr 的解决方案 @MattKlein 对我来说,它看起来解决了它。实际上是一样的,而且不那么冗长。 问题想知道如何将参数传递到类中,就像使用需要参数的构造函数一样。该链接(应该在此处进行总结)仅显示了如何拥有无参数的实例初始化程序,它没有回答问题。如 aav 所述,此技术可与 final 变量一起使用,但此答案中未提供该信息。到目前为止,最好的答案是 Adam Mlodzinksi 给出的答案(我现在只使用这种模式,不再使用决赛!)。我坚持我的评论,这并不能回答所提出的问题。【参考方案7】:

我的解决方案是使用返回实现的匿名类的方法。常规参数可以传递给方法,并且在匿名类中可用。

例如:(从一些 GWT 代码来处理文本框的变化):

/* Regular method. Returns the required interface/abstract/class
   Arguments are defined as final */
private ChangeHandler newNameChangeHandler(final String axisId, final Logger logger) 

    // Return a new anonymous class
    return new ChangeHandler() 
        public void onChange(ChangeEvent event) 
            // Access method scope variables           
            logger.fine(axisId)
        
     ;

对于这个例子,新的匿名类方法将被引用为:

textBox.addChangeHandler(newNameChangeHandler(myAxisName, myLogger))

,使用 OP 的要求:

private ActionListener newActionListener(final int aVariable) 
    return new ActionListener() 
        public void actionPerformed(ActionEvent e) 
            System.out.println("Your variable is: " + aVariable);
        
    ;

...
int myVariable = 1;
newActionListener(myVariable);

【讨论】:

这很好,它将匿名类限制为一些易于识别的变量,并消除了必须使某些变量最终化的可恶。【参考方案8】:

其他人已经回答匿名类只能访问最终变量。但是他们留下了如何保持原始变量非最终变量的问题。 Adam Mlodzinski 提供了一个解决方案,但它非常臃肿。这个问题有一个更简单的解决方案:

如果您不希望 myVariable 是最终的,您必须将它包装在一个新的范围中,如果它是最终的,则无关紧要。

int myVariable = 1;


    final int anonVar = myVariable;

    myButton.addActionListener(new ActionListener() 
        public void actionPerformed(ActionEvent e) 
            // How would one access myVariable here?
            // Use anonVar instead of myVariable
        
    );

Adam Mlodzinski 在他的回答中没有做任何其他事情,而是使用了更多代码。

【讨论】:

这在没有额外范围的情况下仍然有效。它实际上与使用 final 的其他答案相同。 @AdamMlodzinski 不,它实际上与您的答案相同,因为它在私有范围内引入了一个具有原始变量值的新变量。 实际上并不相同。在您的情况下,您的内部类无法更改 anonVar - 因此,效果是不同的。例如,如果您的内部类必须维护某种状态,那么您的代码将不得不使用某种带有 setter 而不是原语的 Object。 @AdamMlodzinski 这不是问题所在。问题是如何访问外部变量而不使自己成为最终变量。解决方案是制作最终副本。当然,很明显可以在侦听器中制作变量的额外可变副本。但首先它没有被问到,其次它不需要任何init 方法。我可以在我的示例中添加一行额外的代码来获得这个额外的变量。如果您是构建器模式的忠实拥护者,请随意使用它们,但在这种情况下它们不是必需的。 我看不出这与使用 final 变量解决方案有何不同。【参考方案9】:

可以使用plain lambdas(“lambda 表达式可以捕获变量”)

int myVariable = 1;
ActionListener al = ae->System.out.println(myVariable);
myButton.addActionListener( al );

甚至是一个函数

Function<Integer,ActionListener> printInt = 
    intvar -> ae -> System.out.println(intvar);

int myVariable = 1;
myButton.addActionListener( printInt.apply(myVariable) );

使用函数是重构装饰器和适配器的好方法,see here

我刚开始学习 lambda,所以如果您发现错误,请随时写评论。

【讨论】:

【参考方案10】:

将一些值放入外部变量的简单方法(不属于匿名类) 是怎么跟! 同样,如果您想获取外部变量的值,您可以创建一个返回所需内容的方法!

public class Example

    private TypeParameter parameter;

    private void setMethod(TypeParameter parameter)

        this.parameter = parameter;

    

    //...
    //into the anonymus class
    new AnonymusClass()

        final TypeParameter parameterFinal = something;
        //you can call setMethod(TypeParameter parameter) here and pass the
        //parameterFinal
        setMethod(parameterFinal); 

        //now the variable out the class anonymus has the value of
        //of parameterFinal

    );

 

【讨论】:

【参考方案11】:

如果“myVariable”是一个字段,您可以使用限定的 this:

public class Foo 
    int myVariable = 1;

    new ActionListener() 
        public void actionPerformed(ActionEvent e) 
            Foo.this.myVariable = 8;
        
    );

【讨论】:

【参考方案12】:

我认为匿名类基本上类似于 lambda,但语法更差......事实证明这是真的,但语法更糟糕,并导致(应该是什么)局部变量渗入包含类。

您可以通过将它们放入父类的字段中来访问非最终变量。

例如

界面:

public interface TextProcessor

    public String Process(String text);

类:

private String _key;

public String toJson()

    TextProcessor textProcessor = new TextProcessor() 
        @Override
        public String Process(String text)
        
            return _key + ":" + text;
        
    ;

    JSONTypeProcessor typeProcessor = new JSONTypeProcessor(textProcessor);

    foreach(String key : keys)
    
        _key = key;

        typeProcessor.doStuffThatUsesLambda();
    

我不知道他们是否已经在 java 8 中解决了这个问题(我被困在 EE 世界并且还没有 8)但是在 C# 中它看起来像这样:

    public string ToJson()
    
        string key = null;
        var typeProcessor = new JSONTypeProcessor(text => key + ":" + text);

        foreach (var theKey in keys)
        
            key = theKey;

            typeProcessor.doStuffThatUsesLambda();
        
    

你也不需要 c# 中的单独接口......我想念它!我发现自己在 Java 中进行了更糟糕的设计并且更多地重复自己,因为您必须在 Java 中添加大量代码+复杂性才能重用某些东西,这比复制和粘贴很多时间更糟糕。

【讨论】:

看起来你可以使用的另一个技巧是拥有一个单元素数组,如此处所述***.com/a/4732586/962696

以上是关于如何将参数传递给匿名类?的主要内容,如果未能解决你的问题,请参考以下文章

如何将参数传递给 Java 线程?

如何使用 jquery 将可变参数传递给匿名函数?

如何将子类作为期望基类的函数的参数传递,然后将该对象传递给指向这些抽象类对象的指针向量?

如何将 argparse 参数传递给类

如何将参数传递给 Apache Apex 中的 application.java 类?

如何将参数传递给python中的嵌套类