可以将接口方法标记为 public
, 将域标记为 public static final
Java 程序设计语言有一个非常重要的内置接口,称为 Cloneable
。如果某个类实现了这个 Cloneable 接口,Object 类中的 clone 方法就可以创建类对象的一个拷贝。
在 Java SE 8 中 ,允许在接口中增加静态方法。理论上讲 ,没有任何理由认为这是不合法的。 只是这有违于将接口作为抽象规范的初衷。
目前为止, 通常的做法都是将静态方法放在伴随类中。在标准库中, 你会看到成对出现的接口和实用工具类, 如 Collection/Collections 或 Path/Paths。
可以为接口方法提供一个默认实现。 必须用 default 修饰符标记这样一个方法。
public interface Comparable<T>{
default int compareTo(T other) { return 0; }
// B y default, all elements are the same
}
默认方法可以调用任何其他方法。例如, Collection 接口可以定义一个便利方法:
public interface Collection
{
int size(); // An abstract method
default boolean isEmptyO
{
return sizeO = 0;
}
}
回调( callback) 是一种常见的程序设计模式。在这种模式中 , 可以指出某个特定事件发生时应该采取的动作。 例如,可以指出在按下鼠标或选择某个菜单项时应该采取什么行动。
然而,由于至此还没有介绍如何实现用户接口,所以只能讨论一些与上述操作类似 ,但比较简单的情况。
clone
方法是 Object 的一个 protected 方法, 这说明你的代码不能直接调用这个方法。 只有 Employee 类可以克隆 Employee 对象。这个限制是有原因的。想想看 Object 类如何实现 clone。它对于这个对象一无所知, 所以只能逐个域地进行拷贝。 如果对象中的所有数据域都是数值或其他基本类型,拷贝这些域没有任何问题、 但是如果对象包含子对象的引用,拷贝域就会得到相同子对象的另一个引用,这样一来, 原对象和克隆的对象仍然会共享一些信息。
void learnInterface() throws CloneNotSupportedException {
Comparable x;
x = new Employee("tqc", 100);
class TestClass implements Cloneable {
Date date;
public TestClass(Date date) {
this.date = date;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
TestClass testClass1 = new TestClass(new Date());
TestClass testClass2 = (TestClass) testClass1.clone();
System.out.println(testClass1.date==testClass2.date);
}
1 ) 默认的 clone 方法是否满足要求 ;
2 ) 是否可以在可变的子对象上调用 clone 来修补默认的 clone 方法 ;
3 ) 是否不该使用 clone。
实际上第 3 个选项是默认选项。 如果选择第 1 项或第 2 项,类必须 :
1 ) 实现 Cloneable 接口 ;
2 ) 重新定义 clone 方法,并指定 public 访问修饰符。
深拷贝:
@Override
protected TestClass clone() throws CloneNotSupportedException {
TestClass cloned=(TestClass) super.clone();
cloned.date = (Date) date.clone();
return cloned;
}
lambda表达式
Java 中 已 经 有 很 多 封 装 代 码 块 的 接 口, 如 ActionListener 或 Comparator 。 lambda 表达式与这些接口是兼容的,
public static <T> void sort(List<T> var0, Comparator<? super T> var1) {
List<String> words = Arrays.asList(new String[]{"fuck", "cnm", "nimasile"});
Collections.sort(words,(String a,String b)->a.length()-b.length());
System.out.println(words);
lambda 表达式可以转换为接口
Jva.util.function 包中有一个尤其有用的接口 Predicate:
public interface Predicate<T>
{
boolean test(T t);
// Additional default and static methods
}
ArrayList
类有一个 removelf
方法, 它的参数就是一个Predicate
。这个接口专门用来传递
lambda 表达式。例如,下面的语句将从一个数组列表删除所有 null 值:
list.removelf(e -> e == null);
List<Integer> removeIf(List<Integer> list, Predicate<Integer> predicate){
List<Integer> retList=new ArrayList<>();
for(Integer i:list){
if(predicate.test(i)){
retList.add(i);
}
}
return retList;
}
List<Integer> numbers=Arrays.asList(new Integer[]{1,2,3});
System.out.println(removeIf(numbers,number->number>1));
表达式 System.out::println
是一个方法引用( method reference ), 它等价于 lambda 表达式
x->System.out.println(x)
。
从这些例子可以看出, 要用: : 操作符分隔方法名与对象或类名。主要有 3 种情况:
•object::instanceMethod
•Class::staticMethod
•Class::instanceMethod
在前 2 种情况中, 方法引用等价于提供方法参数的 lambda 表达式。前面已经提到,System.out::println 等价于 x -> System.out.println(x ) 类似地, Math::pow 等价于(x ,y) ->Math.pow(x, y)对于第 3 种情况, 第 1 个参数会成为方法的目标。例如 ,String::compareToIgnoreCase 等同于 (x, y)-> x.compareToIgnoreCase(y)
@FunctionalInterface
类似于Python的sort(list,key=…)
Arrays.sort(people, Comparator.comparing(Person::getName)) ;
静态 comparing 方法取一个“ 键提取器
” 函数, 它将类型 T 映射为一个可比较的类型( 如 String )。 对要比较的对象应用这个函数, 然后对返回的键完成比较。 例如, 假设有一个Person 对象数组 ,可以如下按名字对这些对象排序:
Arrays.sort(people, Comparator.comparing(Person::getName)) ;
与手动实现一个 Compamtor 相比, 这当然要容易得多。另外, 代码也更为清晰, 因为显然我们都希望按人名来进行比较。
可以把比较器与 thenComparing 方法串起来。例如,
Arrays.sort(people,Comparator.
comparing(Person::getlastName).
thenConiparing(Person::getFirstName)) ;