接口、Lambda表达式与内部类

接口

接口的概念

类是对数据和功能的一组描述,同样,接口也是对数据和功能的一组描述,但是,接口不能被继承,只能被实现。

也就是说一个类如果实现了一个接口,就必须实现接口中规定的所有方法。

可以把接口看成是没有实例域的抽象类。

接口的一些特点:

  • 接口中的方法自动地属于public。因此,在接口中声明方法时,不必提供关键字public。

  • 接口中可以定义常量

  • 接口中不能含有实例域

在编写代码时,使用 implements 关键字表示某个类实现了某个接口。

示例: class Employee implements Comparable

不过在 Java SE 5.0 中,Comparable接口已经改进为泛型类型。

示例:

1
2
3
4
5
6
7
class Employee implements Comparable<Employee>
{
public int compareTo(Employee other)
{
return Double.compare(salary, other.salary);
}
}

接口的特性

接口不是类,不能实例化一个接口。

但是可以声明接口的变量,如:

1
Comparable x;

接口变量必须引用实现了接口的类对象:

1
x = new Employee(...);
  • 可以使用 instanceof 检测一个对象是否实现了某个接口

    1
    if (anObject instanceof Comparable){ ... };
  • 类可以建立层次化的继承关系,接口也可以。

    1
    2
    3
    4
    public interface Movable
    {
    void move(double x, double y)
    }
    1
    2
    3
    4
    public interface Powerd extends Movable
    {
    double milesPerGallon();
    }
  • 接口中不能包含实例域或者静态方法,但却可以包含(静态)常量。

    1
    2
    3
    4
    5
    public interface Powerd extends Movable
    {
    double milesPerGallon();
    double SPEED_LIMIT = 95;
    }

    与接口中的方法都自动地被设置为 public 一样,接口中的域将被自动地设为 public static final

  • 一个类只能拥有一个超类,但是可以实现多个接口

    1
    class Employee implements Cloneable, Comparable

接口与抽象类

接口和抽象类的对比:

  1. 一个子类只能继承一个抽象类,但是可以实现多个接口。
  2. 抽象类可以有实例域,接口不能有实例域。
  3. 抽象类和接口都可以有(静态)成员变量。抽象类的静态成员变量可以有多种访问类型,但是接口中的静态成员变量默认且只能是 public static final
  4. 抽象类可以有构造方法,接口没有构造方法。
  5. 抽象类可以有抽象方法或者具体方法,但是接口只有抽象方法。
  6. 抽象类中的方法可以是 publicprotected,接口方法只有 public

Comparator 接口

Java ArrayList of Object Sort Example (Comparable And Comparator)

对象克隆

lambda 表达式

lambda表达式相当于简化了匿名内部类的写法。

但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口。但是lambda表达式没有这个限制。

lambda表达式通常搭配 函数式接口 进行使用。

lambda的代码块只有一行的时候,可以省略 return ,Java会自动把自动把这一行代码的结果当作返回值 return

进阶内容

java - Implied anonymous types inside lambdas - Stack Overflow

java - Invoking a method of an anonymous class - Stack Overflow

Why can’t Java 7 diamond operator be used with anonymous classes? - Stack Overflow

java - Why can’t diamond infer types on anonymous inner classes? - Stack Overflow

关于 Lambda 的一个问题

1
2
3
4
5
6
7
8
9
10
Predicate<Integer> predicate = IntStream.of(10).mapToObj(int1 -> {
return new Predicate<Integer>() {
@Override
public boolean test(Integer number) {
return number % 2 == 0;
}
};
})
.reduce(Predicate::and)
.get();

上边这段代码,编译时候会报错:

1
2
3
4
5
6
7
8
9
error: method reduce in interface Stream<T> cannot be applied to given types;
.reduce(Predicate::and)
^
required: BinaryOperator<<anonymous Predicate<Integer>>>
found: Predicate::and
reason: argument mismatch; bad return type in method reference
Predicate<Integer> cannot be converted to <anonymous Predicate<Integer>>
where T is a type-variable:
T extends Object declared in interface Stream

深入分析一下,reduce 的方法签名是:

1
Optional<T> reduce(BinaryOperator<T> accumulator);

即入参是一个 T,返回值需要和 T 的类型一致。

但是我们在 reduce 方法中执行的是 Predicate::and,入参是 Predicate,返回值是 Optional<T>

但是实际上我们传给 Predicate::and 的参数是一个实现了 Predicate 接口的匿名类,但是返回值是一个 Predicate 类型的值。

按照 reduce 方法的方法签名要求,我们传入了一个匿名类的对象,返回值也必须是一个匿名类的对象。但是 Predicate::and 的返回值是一个 Predicate 类型的对象,那么就需要把这个 Predicate 类型的对象转换成匿名类的对象才可以,但是匿名类是 Predicate 的子类,如果这么转换,相当于把父类的对象赋值给子类的对象,Java 无法直接做转换。所以报错类型不匹配。

其实这个报错相当于下边的代码:

1
2
List<Integer> list = new ArrayList<>();
ArrayList<Integer> list2 = list;

相当于在把一个父类的引用赋值给一个子类引用。

参考资料

  1. java中的匿名内部类总结 - Nerxious - 博客园
  2. Lambda Expressions (The Java™ Tutorials > Learning the Java Language > Classes and Objects)
  3. 一个接口只有一个实现类,写接口的意义是什么?只写类不写接口不是更简单且清晰吗? - 知乎