如何在Java中编写匿名函数?

98

这真的可能吗?


7
请注意,Java 8 现在支持这种操作 - 参见下面Mark Rotteveel关于Lambda表达式的回答。 - Josiah Yoder
6个回答

84

如果你指的是匿名函数,并且使用的是Java 8之前的版本,那么简而言之,不行。(如果你使用Java 8+,请阅读有关lambda表达式的文章

然而,你可以实现一个带有函数的接口,如下所示:

Comparator<String> c = new Comparator<String>() {
    int compare(String s, String s2) { ... }
};

而且您可以将其与内部类一起使用,以获得几乎匿名的函数 :)


7
还不行。在Java 7中将会变得可行:https://dev59.com/23VC5IYBdhLWcg3woCnN - Ilya Boyandin
2
与此同时,在等待JDK7的过程中,可以使用http://en.wikipedia.org/wiki/Command_pattern在面向对象的上下文中模拟匿名方法。 - gpampara
1
闭包没有被纳入Java 7。 - Thorbjørn Ravn Andersen
5
我认为你应该修改你的答案,因为在Java 8中我们有匿名函数。 - Node.JS

46

这是一个匿名内部类的示例。

System.out.println(new Object() {
    @Override public String toString() {
        return "Hello world!";
    }
}); // prints "Hello world!"

这段代码并不是非常有用,但它展示了如何创建一个继承自Object且覆盖 toString()方法的匿名内部类的实例。

另请参阅


当需要实现一个可能不太可重用(因此不值得重新设计为自己命名的类)的接口时,匿名内部类非常方便。一个有启发性的例子是使用自定义java.util.Comparator<T>进行排序。

下面是一个示例,演示如何根据String.length()String[]进行排序。

import java.util.*;
//...

String[] arr = { "xxx", "cd", "ab", "z" };
Arrays.sort(arr, new Comparator<String>() {
    @Override public int compare(String s1, String s2) {
        return s1.length() - s2.length();
    }           
});
System.out.println(Arrays.toString(arr));
// prints "[z, cd, ab, xxx]"

请注意这里使用的比较减法技巧。需要说明的是,这种技术通常是有问题的:仅当您可以保证它不会溢出时才适用(这样就是String长度的情况)。

另请参阅


5
在一般的Swing应用程序中,大多数其他事件可以在EventListener(子)实现中找到。 - BalusC
@BalusC:添加了一个问题“它们是如何使用的”的链接。 - polygenelubricants
@BalusC:stackoverflow 最近添加了“相关链接”侧边栏,所以我正在尽力利用它。 - polygenelubricants

12

在Java 8中引入了Lambda表达式,您现在可以使用匿名方法。

假设我有一个名为Alpha的类,我想按特定条件过滤Alpha。为此,您可以使用Predicate<Alpha>。这是一个功能接口,它有一个接受Alpha并返回booleantest方法。

假设filter方法具有以下签名:

List<Alpha> filter(Predicate<Alpha> filterPredicate)

使用旧的匿名类解决方案,你需要像这样做:

filter(new Predicate<Alpha>() {
   boolean test(Alpha alpha) {
      return alpha.centauri > 1;
   }
});

使用Java 8的lambda表达式,您可以做到:

filter(alpha -> alpha.centauri > 1);

更详细的信息请查看Lambda表达式教程


2
方法引用也是非常有用的。例如:sort(String::compareToIgnoreCase) http://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html - Josiah Yoder

9

在其他答案中已经实现了使用匿名内部类来实现或扩展现有类型的接口,尽管值得注意的是,可以实现多个方法(通常使用JavaBean风格的事件)。

一个很少被认识的功能是,虽然匿名内部类没有名称,但是它们确实有类型。可以向接口添加新方法。这些方法只能在有限的情况下调用。主要直接在new表达式本身和类内(包括实例初始化程序)中。这可能会让初学者感到困惑,但对递归来说可能很“有趣”。

private static String pretty(Node node) {
    return "Node: " + new Object() {
        String print(Node cur) {
            return cur.isTerminal() ?
                cur.name() :
                ("("+print(cur.left())+":"+print(cur.right())+")");
        }
    }.print(node);
}

我最初使用node而不是curprint方法中编写此内容。 拒绝捕获“隐式final”局部变量?


这里应该声明 nodefinal - BalusC
@BalusC 很好的发现。实际上,我的错误是没有使用cur - Tom Hawtin - tackline
@Tom:+1 很棒的技巧!它在实践中真正被使用了吗?这种特定模式有什么名称吗? - polygenelubricants
据我所知,没有这样的功能。这需要额外的对象!(和一个类。)双括号惯用语也是如此。正确思考的人似乎不介意“执行周围”惯用语。 - Tom Hawtin - tackline
也许它应该被称为“匿名习语”。 - Tom Hawtin - tackline
显示剩余5条评论

2

是的,如果您正在使用Java 8或更高版本,则可以。Java 8使定义匿名函数成为可能,而在以前的版本中这是不可能的。

让我们从Java文档中的示例中了解如何声明匿名函数和类

以下示例HelloWorldAnonymousClasses在本地变量frenchGreeting和spanishGreeting的初始化语句中使用了匿名类,但对于变量englishGreeting的初始化则使用了本地类:

public class HelloWorldAnonymousClasses {
  
    interface HelloWorld {
        public void greet();
        public void greetSomeone(String someone);
    }
  
    public void sayHello() {
        
        class EnglishGreeting implements HelloWorld {
            String name = "world";
            public void greet() {
                greetSomeone("world");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hello " + name);
            }
        }
      
        HelloWorld englishGreeting = new EnglishGreeting();
        
        HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };
        
        HelloWorld spanishGreeting = new HelloWorld() {
            String name = "mundo";
            public void greet() {
                greetSomeone("mundo");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hola, " + name);
            }
        };
        englishGreeting.greet();
        frenchGreeting.greetSomeone("Fred");
        spanishGreeting.greet();
    }

    public static void main(String... args) {
        HelloWorldAnonymousClasses myApp =
            new HelloWorldAnonymousClasses();
        myApp.sayHello();
    }            
}

匿名类的语法

考虑实例化frenchGreeting对象:

    HelloWorld frenchGreeting = new HelloWorld() {
        String name = "tout le monde";
        public void greet() {
            greetSomeone("tout le monde");
        }
        public void greetSomeone(String someone) {
            name = someone;
            System.out.println("Salut " + name);
        }
    };

匿名类表达式包括以下内容:
  • new运算符

  • 要实现的接口名称或要扩展的类的名称。在此示例中,匿名类正在实现接口HelloWorld。

  • 括号,其中包含构造函数的参数,就像普通的类实例创建表达式一样。注意:当您实现一个接口时,没有构造函数,所以您使用一个空的括号,就像这个例子。

  • 一个类声明体,其中允许方法声明但不允许语句。


0

您还可以根据需要使用ConsumerBiConsumer类型。 Consumer接受一个参数,BiConsumer接受两个参数。

public void myMethod() {
  // you can declare it here
  Consumer<String> myAnonymousMethod = s -> {
    System.out.println(s);
  };

  // you can call it here
  muAnonymousMethod.apply("Hello World");
}

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