核心技术:接口、lambda表达式与内部类
跳到导航
跳到搜索
接口
接口(interface),是对类的一组需求描述(一组行为定义):
- 接口的所有方法自动属于public(不用提供public关键字);
- 接口中可以有常量,但是不能有实例域;
- 即,域自动设置为:“public static final”
- 接口中可以有实现方法(Java SE 8 之后),但方法中不能引用实例域;
- 默认:“public abstract”
提供实例域和方法实现,应该由实现接口的类来完成。
实现接口:
- 声明实现给定的接口:
class Employee implements Comparable
- 对接口中的所有方法进行定义;
- 所有方法都必须有实现;
- 实现方法时,必须把方法声明为“public”;
接口的特性
- 接口不是类,不能用“new”进行实例化;
- 可以用接口声明变量,变量只能引用接口实现类的对象;
- 可以使用“instanceof”检查一个对象是否实现了某个特定接口(类似于检查对象是否属于某个类);
- 接口可以被继承,用新的接口扩展旧接口;
接口与抽象类
Java不支持多重继承(multiple inheritance):引入抽象类,避免多重继承的复杂性和低效性。
静态方法
Java 8 之后,允许接口中增加静态方法:
- 静态方法无法实现多态,形式上重写,实际上是新方法。
默认方法
可以为接口方法提供一个默认实现(也可以不实现),并用“default”修饰该方法:
- 可以不用实现接口所有方法,只关注于需要的某个或某几个方法(可以不再使用伴随类):
public interface MouseListener { default void mousedieked(MouseEvent event) {} default void mousePressed(MouseEvent event) {} default void mouseReleased(MouseEvent event) {} default void mouseEntered(MouseEvent event) {} default void mouseExited(MouseEvent event) {} }
- 在为旧接口增加方法时,使用默认方法可以保证“源代码兼容”(source compatible);
- (增加非默认方法,会导致接口现有实现类不能正常工作)
关于伴随类
在JavaAPI 中,你会看到很多接口都有相应的伴随类,这个伴随类中实现了相应接口的部分或所有方法, 如 Collection/AbstractCollection 或 MouseListener/MouseAdapter。在JavaSE 8 中,这个技术已经过时。现在可以直接在接口中实现方法。
使用抽象类(伴随类)对接口进行部分实现,而后用户类并不之间实现接口(需要实现全部方法),而是继承与该抽象类(只重写需要的类即可)。如:
public interface Collection<E> extends Iterable<E> { public abstract class AbstractCollection<E> implements Collection<E> {
public interface MouseListener extends EventListener { public abstract class MouseAdapter implements MouseListener, MouseWheelListener, MouseMotionListener {
- 又见,SpringMVC 中的拦截器(见[1]):
public interface HandlerInterceptor { public interface AsyncHandlerInterceptor extends HandlerInterceptor { public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
解决默认方法冲突
冲突:现在接口中将一个方法定义为默认方法,又在超类或另一个接口中定义了同样的方法;
规则:
- 超类优先:接口中的方法(不论是否默认方法)会被忽略;
- 接口冲突:一个接口提供默认方法,一个接口提供同名同参数方法(不论是否默认方法),则在实现类必须覆盖这个方法;
接口示例
接口与回调
回调的思想是:
- 类A的a()方法调用类B的b()方法
- 类B的b()方法执行完毕主动调用类A的callback()方法
- 回调的核心就是回调方将本身即this传递给调用方
回调与接口:
- 将回调方法抽象为接口,用回调方去实现需要的回调接口
Comparator 接口
- 若需要将对象数组排序,则必须数组的每一个元素都实现了Comparable接口:
package java.lang; import java.util.*; public interface Comparable<T> { public int compareTo(T o); }
然而,如果需要对String数组排序(已经实现了Comparable<String>接口),但又不想按照字典顺序排序(String.compareTo的实现是按字典顺序比较):
- 首先我们不能使String类实现两种不同的compareTo方法,
- 其次String类也不应该由我们修改。
即:如何使用Comparable.compareTo方法之外的比较方法,来进行对象数组的排序?
所以,我们需要用到:
- Arrays.sort 方法
public static <T> void sort(T[] a, Comparator<? super T> c) { if (c == null) { sort(a); } else { if (LegacyMergeSort.userRequested) legacyMergeSort(a, c); else TimSort.sort(a, 0, a.length, c, null, 0, 0); } }
- 以及,Comparator<T>接口
package java.util; ... public interface Comparator<T> { int compare(T o1, T o2); ... }
如:
- 定义一个 Comparator<String> 接口的实现类:
- Comparator<T> 接口的方法很多,但并不用全部实现,因为一部分方法有实现,一部分为默认方法;
class LengthComparator implements Comparator<String> { public int compare(String first, String second) { return first.lengthO - second.lengthO; } }
- 使用Arrays.sort进入数组比较:
String[] friends = { "Peter", "Paul", "Mary" }; Arrays,sort(friends, new LengthComparatorO):
- 也可以,单独对两个对象或两个数组元素进行比较:
Comparator<String> comp = new LengthComparator。; if (conp.compare(words[i], words[j]) > 0)
- Comparator.compare 并非静态方法,所以使用时需要 Comparator实例;
对象克隆
拷贝和克隆
- 为一个包含对象引用的变量建立副本,直接赋值即可,原件副本都是同一个对象的引用;
- 若希望为对象也创建一个副本,则应该使用克隆;
浅拷贝(Object.clone())
默认的克隆操作(“Object.clone()”)是“浅拷贝”:如果对象包含子对象的引用,拷贝域会得到相同子对象的另一个引用;
- 即,原对象域浅克隆对象会引用相同的子对象;
- Object.clone() 是Object的一个protected方法;
- 若,子对象属于一个不可变的类,或在生命周期中状态不变,则是安全的;否则不应该使用浅拷贝;
深拷贝(Cloneable 接口)
Cloneable 接口时Java提供的一组标记接口(tagging interface,或称记号接口 marker interface):
- 标记接口不包含任何方法,它的唯一作用就是允许在类型查询中使用 instanceof;
- 即使 clone 的默认实现(浅拷贝)满足要求,还是需要实现 Cloneable 接口,将 clone 重新定义为 public,再调用 super.clone():
class Employee implements Cloneable
{
// raise visibility level to public, change return type
public Employee clone() throws CloneNotSupportedException
{
return (Employee) super.clone();
}
...
}
深克隆的实现:
- 实现 Cloneable 接口,将 clone 重新定义为 public;
- 重写 clone 方法,克隆需要的子对象(分别调用子对象的克隆方法);
class Employee implements Cloneable
{
...
public Employee cloneO throws Cl oneNotSupportedExcepti on
{
try {
// call Object, clone0
Employee cloned = (Employee) super.cloneO ;
// clone mutable fields
cloned.hireDay = (Date) hireDay.clone() ;
return cloned;
} catch (CloneNotSupportedException e) {
return null;
}
// this won't happen, since we are Cloneable
}
}