Java核心技术卷一基础知识第10版

#接口、lambda 表达式与内部类

#接口

  • 接口中的所有方法自动地属于 public,在接口中声明方法时不必提供关键字 public
  • 接口绝不能含有 实例域。提供实例域和方法实现的任务应该由实现接口的那个类来完成。
  • 为了让类实现一个接口,需要:
    1. 将类声明为实现给定的接口。
      要将类声明为实现某个接口,需要使用关键字 implements
    1
    class Employee implements Comparable
    1. 对接口中的所有方法进行定义。
  • 如果存在这样一种通用算法,它能够对两个不同的子类对象进行比较,则应该在超类中提供一个 compareTo 方法,并将这个方法声明为 final
  • 接口不是类,不能使用 new 运算符实例化一个接口,但是可以声明接口的变量,接口变量必须引用实现了接口的类对象。
    1
    2
    3
    x = new Comparable(...); // ERROR
    Comparable x; // OK
    x = new Employee(...); // OK provided Employee implements Comparable
  • 可以使用 instance 检查一个对象是否实现了某个特定的接口。
    1
    if (anObject instanceof Comparable) {...}
  • 接口中的域将被自动设为 public static final
  • 在实现接口时,必须把方法声明为 public,否则,编译器将认为这个方法的访问属性是包可见性,即类的默认访问属性,之后编译器就会给出试图提供更严格的访问权限的警告信息。
  • 每个类可以实现多个接口。
  • 可以为接口方法提供一个默认实现,必须用 default 修饰符标记这样一个方法。
  • 默认方法的一个重要用法是 接口演化(interface evolution),保证不会对旧代码产生影响。
  • 如果先在一个接口中将一个方法定义为默认方法,然后又在超类或另一个接口中定义了同样的方法,遵循如下规则:
    1. 超类优先。
      如果超类提供了一个具体方法,同名而且有相同参数类型的默认方法会被忽略。
    2. 接口冲突。
      如果一个超接口提供了一个默认方法,另一个接口提供了一个同名而且参数类型(不论是否是默认参数)相同的方法,必须覆盖这个方法来解决冲突。

接口与回调可能在这里不太看得懂,暂时跳过。[1]


#lambda 表达式

  • lambda表达式就是一个代码块, 以及必须传入代码的变量规范。
    1
    2
    (String first, String second)
    -> first.length() - second.length()
  • 如果代码要完成的计算无法放在一个表达式中,就可以像写方法一样,把这些代码放在 {} 中, 并包含显式的 return 语句。
    1
    2
    3
    4
    5
    6
    (String first, String second) ->
    {
    if (first.length() < second.length()) return -1;
    else if (first.length() > second.length()) return 1;
    else return 0;
    }
  • 即使 lambda 表达式没有参数,仍然要提供空括号,就像无参数方法一样。
    1
    () -> { for (int i = 100; i >= 0; i-- ) System.out.println(i); }
  • 如果可以推导出一个 lambda 表达式的参数类型,则可以忽略其类型。
    1
    2
    3
    Comparator<String> comp
    = (first, second) // Same as (String first, String second)
    -> first.length() - second.length();
  • 如果方法只有一个参数,而且这个参数的类型可以推导得出,那么甚至还可以省略小括号。
  • 无需指定 lambda 表达式的返回类型。lambda 表达式的返回类型总是会由上下文推导得出。
  • 要用 :: 操作符分隔方法名与对象或类名主要有3种情况:
    • object::instanceMethod
    • Class::staticMethod
    • Class::instanceMethod
  • 无法构造泛型类型 T 的数组。(数组构造器引用对于克服这个限制很有用。)
    1
    2
    Object[] people = stream.toArray(); // Object数组引用
    Person[] people = stream.toArray(Person[]::new); // Person数组引用
  • lambda 表达式中捕获的变量必须实际上是最终变量(effectively final)
  • lambda 表达式的体与嵌套块有相同的作用域。
  • 在 lambda 表达式中声明与一个局部变量同名的参数或局部变量是不合法的。
    1
    2
    3
    4
    Path first = Paths.get("/usr/bin"); 
    Comparator<String> comp =
    (first, second) -> first.length() - second.length();
    // Error: Variable first already defined
  • 在一个 lambda 表达式中使用 this 关键字时,是指创建这个 lambda 表达式的方法的 this 参数。
  • 使用 lambda 表达式的重点是延迟执行(deferred execution)

#内部类

  • 使用内部类的原因:
    • 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
    • 内部类可以对同一个包中的其他类隐藏起来。
    • 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。
  • 使用外围类引用的正规语法为 OuterClass.this

好晕啊😭,看不懂。[2]

代理也待看[3]


  1. 待看 ↩︎

  2. 待看 ↩︎

  3. 待看 ↩︎