笔记:Java接口

接口

接口概念

接口不提供实例字段和实现,但可以声明变量;接口是对希望符合这个接口的类的一组需求。
如果要调用Arrays类中的sort方法,那么该类需要满足以下条件:

interface Comparable<T> {
	int compareTo(T other);
}

class Employee implements Comparable<Employee> {  // 泛型
	...  // method, fields
	public int compareTo(Employee other) { 
		// 用该对象和other比较。小于返回负数,大于返回整数,相等返回0
		return Double.compare(salary, other.salary);
	}
	
	public static void main(String[] args) {
		var staff = new Employee[3];
		...
		Arrays.sort(staff);  // 排序
		...
}

实现接口的原因及其解释

要让staff使用sort,必须让Employee类实现compareTo方法,因为要向sort方法提供对象的比较方式。因为Java是强类型的语言,在调用方法的时候,编译器要能检查这个方法确实存在;staff是一个Employee数组,而只有Employee实现了comparable接口,才可以确定有compareTo方法,因为每个实现Comparable接口的类都必须提供compareTo的定义,所以该方法存在,才可以调用。

接口的属性

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

Comparable x = new Employee(...);
// 使用instanceof检查对象引用是否实现了接口
if (x instanceof Comparable) {...}

接口不能包含实例字段,但可以包含常量:

interface Moveable {
	void move(double x, double y);
}

interface Powered extends Moveable {
	double milesPerGallon();
	double SPEED_LIMIT = 95;  // 接口常量都是public static final
}

默认方法

可以为接口方法提供一个默认实现:

interface Comparable<T> {
	default int compareTo(T other) { return 0;}
}

用法:

  1. 比如Iterator接口,在传入的迭代器是只读无法修改的情况下,那么Iterator接口内的修改操作(方法)如果是default的,就不用再去实现修改操作的方法
interface Iterator<E> {
	boolean hasNext();
	E next();
	default void remove() { throw new UnsupportedOperationException("remove");}
	...
}
  1. 默认方法可以调用其他方法
interface Collection {
	int size();  // abstract method
	default boolean isEmpty() { return size == 0;}  // 就不用操心实现isEmpty方法
  1. 接口演化
    当以前你写的类实现一个接口,现在接口又新增了新的方法导致你的类现在没办法编译(因为新的方法没有在你以前写的类里面实现),这个时候如果设新的方法为default,就解决问题了。

解决默认方法冲突

二义性:已有一个接口将一个方法定位default,然后又有父类或另一个接口中定义同样的方法

  1. 父类优先:如果父类提供了具体方法,那签名相同的默认方法会被忽略;
public abstract Person {
	public String character();
	public String getName() { return "";}  // got implementation!
}

interface NickName {
	default String getName() { return "little" + "";}  // ignored!
}

class Student extends Person implements NickName {...}
  1. 接口冲突:如果一个接口提供了一个默认方法,另一个接口提供签名相同(default or not)的方法,必须覆盖掉这个方法来解决冲突。Student类会继承Person和Named接口提供的两个不一致的getName,只要在Student类中实现掉其中一个接口的getName方法即可。不管Person和Named两个类中的相同方法是否是默认,只要至少有一个接口提供了一个实现,就要解决冲突
interface Person {
	default String getName() { return "";}
}

interface Named {
	default String getName() { return getClass().getName() + "_" + hashCode();}
}

interface NickName {
	String getName();  // no default
}

class Student implements Person, Named, NickName { // solution
	public String getName() { return Person.super.getName();}
	// or Named.super.getName() or NickName.super.getName()
}

设计模式:回调(callback)

一种设计模式:可以指定某个特定事件发生时应该采取的动作

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Instant;
import static toolDIY.Print.*;

public class TimerTest {
    public static void main(String[] args) {
        var listener = new TimePrinter();  // 声明一个监听器对象

        // 构造一个定时器隔一定时间调用监听器对象
        var timer = new Timer(1000, listener);
        // lambda表达式:
        var timer = new Timer(1000, event ->
        				pln("The time is " + new Date()));
        timer.start();
        
        // 在swing包里的方法,显示对话框
        JOptionPane.showMessageDialog(null, "Quit program?");
        System.exit(0);
    }
}

class TimePrinter implements ActionListener {  // 实现监听接口
    public void actionPerformed(ActionEvent event) {
        // 获取事件发生时的时间, 返回1970-1-1以来的毫秒数传入Instant.ofEpochMilli
        println("At the tone, the time is "
                + Instant.ofEpochMilli(event.getWhen()));

        // awt的工具箱包含有关GUI环境的信息,awt是创建用户界面和绘制图形的包
        Toolkit.getDefaultToolkit().beep();
    }
}

Comparator接口

Arrays.sort提供第二个比较版本,一个数组和一个比较器作为参数,比较器实现Comparator接口:

import java.util.Arrays;
import java.util.Comparator;

import static toolDIY.Print.*;

public class Test implements Comparator<String> {  // 实现Comparator接口
    public int compare(String first, String second) {
        return first.length() - second.length();
    }

    public static void main(String[] args) {
        String[] friend = {"Peter", "Mike", "Amy"};
        Arrays.sort(friend, new Test());  // 排序
        // lambda 表达式:
        Arrays.sort(friend, (first, second) -> 
        			first.length() - second.length());
        println(Arrays.toString(friend));
    }
}

对象克隆

有原对象a,新对象b;如果要让b初始状态和a相同,但之后它们各自会有自己不同的状态:

var original = new Employee(...);
Employee copy = original.clone();
copy.raiseSalary();  // original unchanged

但是如果对象包含子对象引用,拷贝字段就会得到相同子对象的另一个引用,这样一来,原对象和克隆对象仍然会共享一些信息;默认的克隆操作是“浅拷贝”,没有克隆对象中引用的其他对象。要实现Cloneable接口,重新定义clone方法,指定public访问修饰符。Cloneable接口是标记接口(tagging interface),不包含任何方法,唯一的作用就是允许使用instanceof。
深拷贝的一个例子:

class Employee implements Cloneable {
	private String name;  // 基本类型是不可变的,默认拷贝不影响
	private Double salary;
	private Date hireDay;  // Date是可变的,localDate是不可变的
	
	public Employee clone() throws CloneNotSupportedException {
		// call Object.clone()
		Employee cloned = (Employee) super.clone();
		// 深拷贝,拷贝可变字段
		cloned.hireDay = (Date) hireDay.clone();
		
		return cloned;
	}
}

所有数组类型都有一个公共的clone方法,可以用它建立一个新数组,且新数组状态不影响原数组状态:

public class Test {
    public static void main(String[] args) {
        int[] original = {2, 3, 4, 6, 7};
        int[] copy = original.clone();
        copy[3] = 5;

        println(Arrays.toString(original));  // [2, 3, 4, 6, 7]
        println(Arrays.toString(copy));  // [2, 3, 4, 5, 7]
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值