Lambda表达式用于抽象类

47

我有一个抽象类,它只有一个抽象方法。如何使用lambda表达式来实例化它?由于它继承了一个类,所以无法将其转换为接口。

public class Concrete<T> {
    // Has a bunch of predefined methods.
}


public abstract class Abstract<T> extends Concrete<T> {
    public T getSomething();
    // Uses inherited methods from Concrete class
}

public class Driver {
    public static void main(String[] args) {
        System.out.println(new Abstract<String>() {
            public String getSomething() {
                // Returns something using inherited methods from Abstract              
                // Class and Concrete Class
            }
        });
    }
}

2
在这种情况下,包含一个 MCVE 会很有帮助。其中你有一个超类、抽象类和一个实例化对象,你想将其简化为一个 lambda 表达式。 - luk2302
为什么不能为getSomething()创建一个接口,让抽象类扩展Concrete并实现Interface? - Jan
4个回答

56

正如Sleiman Jneidi在他的回答中指出的那样,您不能直接使lambda表达式针对抽象类。但是,您可以使用一个解决方法:

public class AbstractLambda<T> extends Abstract<T>
{
    private final Supplier<? extends T> supplier;
    public AbstractLambda(Supplier<? extends T> supplier)
    {
        this.supplier = supplier;
    }

    @Override
    public T getSomething()
    {
        return this.supplier.get();
    }
}

这可以与lambda表达式一起使用:
Abstract<String> a = new AbstractLambda<>(() -> "Hello World");
System.out.println(a.getSomething()); // prints 'Hello World'

如果你的getSomething(...)方法有参数,请使用java.util.function.Functionjava.util.function包中适当的接口,而不是java.util.function.Supplier
这也是java.lang.Thread让你使用Runnable lambda而不必子类化该类的方式。
Thread t = new Thread(() -> System.out.println("Hello World"));
t.start();

2
我不同意Thread的定义方式类似。将Runnable作为构造函数参数传递给Thread是自然的,而且Runnable是一个简单的FunctionalInterface。它看起来并不像这个解决方法。 - snap
4
它们很相似,唯一的区别是Thread不是抽象类,但除此之外它们的模式是相同的:要么扩展Thread并覆盖run()方法,要么将一个Runnable传递给Thread构造函数。 - Alejandro C De Baca

46
不行,你不能这样做。Lambda表达式必须针对接口上的单个抽象方法(SAM),并且它们不能与抽象类上的单个抽象方法一起使用。就是这样,你必须接受它。
尽管有这种需求是有意义的,但语言设计者决定不值得引入允许在 SAM 抽象类上使用 lambda 表达式所带来的复杂性。 作为参考,这是 Brian Goetz 对于允许在 SAM 抽象类上使用 lambda 表达式的看法。 Brian 邮件中的主要观点:
  • 仅有 3% 的 lambda 候选内部类实例的目标是抽象类

  • 为了少数情况的应用而使模型变得复杂似乎是一个糟糕的交易


这就是事实,你必须接受它。不,我不会接受它。我不能接受它。 - user64141

0
如先前所述,你不能这样做。
但是还有另外一种替代方案可能对某些人有用:
根据你的设计,考虑使用带有默认函数的接口而不是抽象类:
这个抽象类:
public abstract class RandomNumber {
    
    public abstract int createRandom(int smalles, int highest);

    public int rollTheDice() {
        return createRandom(1, 6);
    }

}

作为一个接口:

public interface RandomNumber {
    
    public int createRandom(int smalles, int highest);

    public default int rollTheDice() {
        return createRandom(1, 6);
    }

}

现在可以写成:

RandomNumber rn = (min, max) -> Math.random()*(max-min) + min;

0
与此同时,我们现在在接口中拥有默认实现。我曾经处于类似的情况,尝试使用抽象类。转而使用了一个具有两个方法的接口,其中一个方法具有默认实现。Java将该接口标识为具有单个抽象方法,我可以使用lambda语法将接口的实现作为参数传递。
缺点是,如果某人坚持要这样做,他们可以通过在某个(匿名或非匿名)类中显式实现接口来覆盖具有默认实现的方法。您无法限制它,因为在接口上所有方法都是公共的。根据您的情况,这可能并不重要。

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