Lambda表达式可以替代多态吗?

时间:2018-07-31 04:52:49

标签: java lambda java-8

我正在学习lambda表达式和功能接口。我们可以通过lambda表达式直接编写接口的实现。所以我认为,它可能是多态性的替代方法。

我有一些使用多态的代码,

interface Drawable {
    public void draw();
}


class Shape {

    protected String name;

    public Shape(String name) {
        this.name = name;
    }
}

class Rectangle extends Shape implements Drawable  {

    public Rectangle(String name) {
        super(name);
    }

    @Override
    public void draw() {
        System.out.println("I am "+this.name);
        System.out.println("Drawing rectangle with 2 equal sides.");
    }
}

class Square extends Shape implements Drawable {

    public Square(String name) {
        super(name);
    }

    @Override
    public void draw() {
        System.out.println("I am "+this.name);
        System.out.println("Drawing square with 4 equal sides.");
    }
}


public class DrawShape {

    public static void main(String ar[]) {

        Drawable rectangle = new Rectangle("Rectangle");
        rectangle.draw();

        Drawable square = new Square("Square");
        square.draw();

    }
}

我已经使用lambda表达式和功能接口编写了以上代码,

@FunctionalInterface
interface Drawable {
    public void draw();
}


class Shape {
    private String name;
    public Shape(String name) {
        this.name = name;
    }

    public void draw(Drawable d1) {
        System.out.println("I am "+this.name);
        d1.draw();
    }
}



public class DrawShape {

    public static void main(String[] args) {
        Shape s1 = new Shape("Rectangle");
        Drawable rectangle = () -> System.out.println("Drawing rectangle with 2 equal sides.");
        s1.draw(rectangle);

        Shape s2 = new Shape("Square");
        Drawable sqaure = () -> System.out.println("Drawing square with 4 equal sides.");
        s2.draw(sqaure);
    }

}

哪种方法更好? 那其他方面呢,例如lambda的代码可重用性,代码维护和修改,耦合和内聚等?

5 个答案:

答案 0 :(得分:11)

我认为lambda表达式允许开发人员像完全类实现那样编写完全多态类型。

多态性通常以两种方式出现:

Drawable drawable = new Rectangle("name");
drawable.draw();
Shape shape = (Shape) drawable; //same object, multiple types.

并且:

Drawable drawable2 = new Rectangle("name");
drawable2.draw(); //Rectangle.draw() implementation invoked
drawable2 = new Square("name");
drawable2.draw(); //Square.draw() implementation

lambda表达式完全不允许这两个:

  1. Lambda表达式将仅用于实现功能接口。这是第一个主要限制。
  2. 尽管可以做到这一点:

    Drawable drawable = () -> System.out.println("drawing rectangle");
    drawable = () -> System.out.println("drawing square");
    

    这与上面的第二个代码段完全不同(在一个更复杂的示例中,一个代码能够在Shape中提供基本的实现,并在Rectangle中对其进行覆盖, Square;而lambda则无法实现。同样,认为上述两个分配使用不同的源代码是正确的。

  3. 一个人不能像类一样“投射”类型:

    Drawable drawable3 = () -> System.out.println("Drawing something");
    Shape shape3 = (Shape) drawable3; //Class cast exception.
    

换句话说,lambda表达式非常适合功能编程编码,可以替代良好的面向对象设计。< / p>

答案 1 :(得分:3)

我同意@yelliver和@ cricket_007,您所做的只是使用匿名类而不是子类。

您写了

Shape s1 = new Shape("Rectangle");
Drawable rectangle = () -> System.out.println("Drawing rectangle with 2 equal sides.");
s1.draw(rectangle);

就像

Shape s1 = new Shape("Rectangle");
Drawable rectangle = new Drawable() {
    @Override
    public void draw() {
        System.out.println("Drawing rectangle with 2 equal sides.");
    }
};
s1.draw(rectangle);

因此,您创建的两个示例并不相同。您的第一个示例使用子类,而第二个示例使用匿名类。

  

所以我认为,它可能是多态性的替代方法

显然,这不是替代方案,它是另一种实现方式。

Java中 lambda expression 的优点。

1。更少的代码行

在上面的示例中,您可以看到仅-> 更改了匿名类。

2。通过在方法中传递行为来支持顺序和并行执行

您可以仅使用foreach()来迭代任何集合。 ( example

list.forEach(item->System.out.println(item));

3。 Lambda表达式和对象

在Java中,任何lambda表达式都是对象,就像功能接口的实例一样。我们可以将lambda表达式分配给任何变量,然后像其他任何对象( example )一样传递它。喜欢

list.sort((Developer o1, Developer o2)->o1.getAge()-o2.getAge());

答案 2 :(得分:2)

Java 8中的

lambda-expressions表示功能接口(仅具有1个方法的接口)的实例。在第二种方法中,您只需创建一个匿名类。如果要重用代码,这没有任何意义。有关更多信息,您可以参考为什么/何时使用匿名类:How are Anonymous (inner) classes used in Java?

public static void main(String[] args) {
    Shape s1 = new Shape("Rectangle");
    Drawable rectangle = new Drawable() {
        @Override
        public void draw() {
            System.out.println("Drawing rectangle with 2 equal sides.");
        }
    };
    s1.draw(rectangle);

    Shape s2 = new Shape("Square");
    Drawable sqaure = new Drawable() {
        @Override
        public void draw() {
            System.out.println("Drawing square with 4 equal sides.");
        }
    };
    s2.draw(sqaure);
}

答案 3 :(得分:2)

命令式风格和功能式风格各有利弊。在这种特定情况下,使用功能样式方法会更好,因为您无需为需要封装单个功能的每种情况都创建一个类。

很快就会发现,对于需要封装单个功能的每种情况创建一个类都会导致不必要的样板代码

使用命令式方法,您可以编写代码并随时进行编写。甚至可能最终在不完全了解实现方式的情况下在类上编写类,这可能会导致庞大且不可持续的代码库。

另一方面,功能性方法迫使人们在编码之前和编码期间更好地了解它们的实现。

那是非常重要的,要理解功能编程本身不应该被视为面向对象编程的“替代品”。

当业务逻辑变得更加复杂时,您实际上可能会发现自己回到了OOP方法。

答案 4 :(得分:1)

我认为,第二种方法更加灵活,因为绘图逻辑与形状本身是分开的。

因此,让我们专注于第二种方法。 @FunctionalInterface是可选的,因为任何给定的接口只有一个单一的抽象方法(也称为SAM接口),就被视为功能接口

正如我所说,第二种方法更有利,因为任何促进代码分离的给定方法都会导致可重用,可维护和松耦合的解决方案。

您的第二个解决方案更倾向于采用“行​​为传递”方法。一种实现此方法的方法(特别是如果您的行为可以表示为一个微小的单个表达式)是 lambda表达式

例如,给定一个根据给定上下文绘制的矩形,您可以创建两个单独的lambda表达式(行为),并在上下文更改时将它们传递给绘制逻辑。

    Shape s1 = new Shape("Rectangle");

    s1.draw(() -> System.out.println("Drawing rectangle in context 1."));

    // context changes

    s1.draw(() -> System.out.println("Drawing rectangle in context 2."));

在第一个示例中,您不能尝试上面的代码片段,因为图形本身是硬编码形状。

总而言之,始终喜欢使用解耦解决方案,并且如果要解耦的行为可以表示为微小的单个表达式,则解决问题的最佳方法是使用lambda表达式,或者如果您的行为已在其他地方实现,则可以method references的优势。