核心技术:接口、lambda表达式与内部类

来自Wikioe
Eijux讨论 | 贡献2020年10月17日 (六) 17:29的版本 →‎接口示例
跳到导航 跳到搜索


接口

接口(interface),是对类的一组需求描述(一组行为定义):

  1. 接口的所有方法自动属于public(不用提供public关键字);
  2. 接口中可以有常量,但是不能有实例域;
    即,域自动设置为:“public static final”
  3. 接口中可以有实现方法(Java SE 8 之后),但方法中不能引用实例域;
    默认:“public abstract”

提供实例域和方法实现,应该由实现接口的类来完成。

实现接口:

  1. 声明实现给定的接口:
    class Employee implements Comparable
    
  2. 对接口中的所有方法进行定义;
  • 所有方法都必须有实现;
  • 实现方法时,必须把方法声明为“public”;

接口的特性

  1. 接口不是类,不能用“new”进行实例化;
  2. 可以用接口声明变量,变量只能引用接口实现类的对象;
  3. 可以使用“instanceof”检查一个对象是否实现了某个特定接口(类似于检查对象是否属于某个类);
  4. 接口可以被继承,用新的接口扩展旧接口;

接口与抽象类

Java不支持多重继承(multiple inheritance):引入抽象类,避免多重继承的复杂性和低效性。

静态方法

Java 8 之后,允许接口中增加静态方法:

  • 静态方法无法实现多态,形式上重写,实际上是新方法。

默认方法

可以为接口方法提供一个默认实现(也可以不实现),并用“default”修饰该方法:

  1. 可以不用实现接口所有方法,只关注于需要的某个或某几个方法(可以不再使用伴随类):
    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) {}
    }
    
  2. 在为旧接口增加方法时,使用默认方法可以保证“源代码兼容”(source compatible);
    (增加非默认方法,会导致接口现有实现类不能正常工作)


关于伴随类

在JavaAPI 中,你会看到很多接口都有相应的伴随类,这个伴随类中实现了相应接口的部分或所有方法, 如 Collection/AbstractCollection 或 MouseListener/MouseAdapter。在JavaSE 8 中,这个技术已经过时。现在可以直接在接口中实现方法。

使用抽象类(伴随类)对接口进行部分实现,而后用户类并不之间实现接口(需要实现全部方法),而是继承与该抽象类(只重写需要的类即可)。如:

  1. public interface Collection<E> extends Iterable<E> {
    
    public abstract class AbstractCollection<E> implements Collection<E> {
    
  2. public interface MouseListener extends EventListener {
    
    public abstract class MouseAdapter implements MouseListener, MouseWheelListener, MouseMotionListener {
    
  3. 又见,SpringMVC 中的拦截器(见[1]):
    public interface HandlerInterceptor {
    
    public interface AsyncHandlerInterceptor extends HandlerInterceptor {
    
    public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
    

解决默认方法冲突

冲突:现在接口中将一个方法定义为默认方法,又在超类或另一个接口中定义了同样的方法;
规则:

  1. 超类优先:接口中的方法(不论是否默认方法)会被忽略;
  2. 接口冲突:一个接口提供默认方法,一个接口提供同名同参数方法(不论是否默认方法),则在实现类必须覆盖这个方法;

接口示例

接口与回调

回调的思想是:

  1. 类A的a()方法调用类B的b()方法
  2. 类B的b()方法执行完毕主动调用类A的callback()方法
  • 回调的核心就是回调方将本身即this传递给调用方


回调与接口:

  1. 将回调方法抽象为接口,用回调方去实现需要的回调接口


关于Java调用

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方法之外的比较方法,来进行对象数组的排序?


所以,我们需要用到:

  1. 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);
            }
        }
    
  2. 以及,Comparator<T>接口
    package java.util;
    ...
    public interface Comparator<T> {
        int compare(T o1, T o2);
        ...
    }
    


如:

  1. 定义一个 Comparator<String> 接口的实现类:
    Comparator<T> 接口的方法很多,但并不用全部实现,因为一部分方法有实现,一部分为默认方法;
    class LengthComparator implements Comparator<String>
    {
        public int compare(String first, String second) {
            return first.lengthO - second.lengthO
        }
    }
    
  2. 使用Arrays.sort进入数组比较:
        String[] friends = { "Peter", "Paul", "Mary" };
        Arrays,sort(friends, new LengthComparatorO):
    
  3. 也可以,单独对两个对象或两个数组元素进行比较:
        Comparator<String> comp = new LengthComparator。;
        if (conp.compare(words[i], words[j]) > 0)
    
  • Comparator.compare 并非静态方法,所以使用时需要 Comparator实例;

对象克隆

拷贝和克隆

拷贝和克隆.png

  1. 为一个包含对象引用的变量建立副本,直接赋值即可,原件副本都是同一个对象的引用;
  2. 若希望为对象也创建一个副本,则应该使用克隆;

浅拷贝(Object.clone()) 

浅拷贝.png

默认的克隆操作(“Object.clone()”)是“浅拷贝”:如果对象包含子对象的引用,拷贝域会得到相同子对象的另一个引用;

  • 即,原对象域浅克隆对象会引用相同的子对象;
  1. Object.clone() 是Object的一个protected方法;
  2. 若,子对象属于一个不可变的类,或在生命周期中状态不变,则是安全的;否则不应该使用浅拷贝;

深拷贝(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();
    }
    ...
}


深克隆的实现:

  1. 实现 Cloneable 接口,将 clone 重新定义为 public;
  2. 重写 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
	}
}

lambda

内部类

代理