接口
: 不是类, 而是对希望符合这个接口的类的一组需求
Arrays
类中的sort
方法对对象数组进行排序, 要求对象所属的内必须实现Comparable
接口
public interface Comparable
{
int compareTo(Object other);
}
//java5后
public interface Comparable<T>
{
int compareTo(T other);
}
public
implements
class Employee implements Comparable
Java API
建议equals, compareTo
方法兼容x = BigDecimal("1.0"); y = BigDecimal("1.00"); x.equals(y)//false x.compareTo(y) == 0
package chapter6_interface_lambda_innerClass.interfaces;
public class Employee implements Comparable<Employee>{
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public double getSalary() {
return salary;
}
public String getName() {
return name;
}
public int compareTo(Employee other) {
return Double.compare(salary, other.salary);
}
}
package chapter6_interface_lambda_innerClass.interfaces;
import java.util.Arrays;
public class EmployeeSortTest {
public static void main(String[] args) {
var staff = new Employee[3];
staff[0] = new Employee("Oukunnan", 25598);
staff[1] = new Employee("Ovfdunnan", 18);
staff[2] = new Employee("dsukunnan", 98);
Arrays.sort(staff);
for (Employee employee : staff) {
System.out.println(employee.getName() + " salary=" + employee.getSalary());
}
}
}
new
运算符实例化一个接口Comparable x ; //OK
x = new Employee(...);
instanceof
: 1. 检查一个对象是否属于某个特定的类 2. 一个对象是否实现了某个特定的接口if (anObject instanceof Comparable){...};
public interface Moveable{...}
public interface Powered extends Moveable{..}
public
, 接口的字段总是public static final
, 都可以省略, 建议省略C++
的多重继承Collection/Collections, Path/paths
private
default
修饰符标记public interface Comparable<T>
{
default int CompareTo(T other) {return 0;}
}
如果迭代器是只读的就不用实现remove
方法
public interface Iterator<E>
{
boolean hasNext();
E next();
default void remove(){throw new UnsupprotedOperationException("remove");}
}
另一个作用是接口演化, 实现源代码兼容
class Student extends Person implements Named{...}
只会考虑超类方法, 类优先原则回调
: 指定某个特定事件发生时应该采取的动作package chapter6_interface_lambda_innerClass.timer;
import java.awt.event.*;
import java.awt.*;
import java.time.*;
import javax.swing.*;
public class TimerTest {
public static void main(String[] args) {
var listener = new TimePrinter();
var timer = new Timer(1000, listener);//每隔1000ms(1s)响铃并且打应输出
timer.start();
JOptionPane.showMessageDialog(null, "Quit Program?");//展示消息框
System.exit(0);
}
}
class TimePrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
//ActionEvent 事件参数,提供事件的相关信息 //getWhen的得到纪元以来的毫秒数, 利用函数转换成可读时间
System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));
Toolkit.getDefaultToolkit().beep(); //调用默认工具箱响铃
}
}
Comparator
接口Comparable
接口类的实例Arrays.sort
的另一种版本, 一个数组和比较器作为参数Comparator
接口public interface Comparator<T>
{
int compare(T first, T second);
}
class LengthComparator implements Comparator<String>
{
public int compare(String first, String second)
{
return first.length() - second.length();
}
}
调用
Arrays.sort(words, new LengthComparator());
clone
方法建立深拷贝, 克隆所有对象Cloneable
: 标记接口, 不包含任何方法(一般的接口确保一个类实现一组特定的方法), 作用:允许类型查询中使用instanceof
if(obj instanceof Cloneable) ...
clone
方法, 不是受保护的int[] a = {1, 2, 3};
int[] b = a.clone();
b[0] = 5;// a[0] == 1;
package chapter6_interface_lambda_innerClass.clone;
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException{
var original = new Employee("Ou Kunnan", 52000);
original.setHireDay(2001, 12, 26);
Employee copy = original.clone();
System.out.println(copy); // 自动调用toString方法,相当于copy.toString()
copy.raiseSalary(10);
copy.setHireDay(2002, 12, 26);
System.out.println("original: " + original);
System.out.println("copy: " + copy);
}
}
package chapter6_interface_lambda_innerClass.clone;
import java.util.Date;
import java.util.GregorianCalendar;
public class Employee implements Cloneable{
private double salary;
private String name;
private Date hireDay;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
hireDay = new Date();
}
public Employee clone() throws CloneNotSupportedException {
Employee cloned = (Employee) super.clone();
cloned.hireDay = (Date) hireDay.clone();
return cloned;
}
public void setHireDay(int year, int month, int day) {
Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();
hireDay.setTime(newHireDay.getTime());
}
public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString() {
return "Employee[name=" + name + ", salary=" + salary + ", hireDay=" + hireDay + "]";
}
}
lambda
表达式lambda
表达式就是一个代码块,以及必须传入的代码变量规范
形式: 参数, ->
, 一个表达式
(String first, String second)->{
if(first.length() < second.length()) return -1;
else return 1;
}
lambda没有参数,`()-> {…};
如果可以推导出lambda
表达式参数类型, 可以忽略其类型
Comparator<String> comp = (first, second) -> first.length() - second.length();
只有一个参数并且类型可以推到, 可以省略小括号
无需指定返回类型
package chapter6_interface_lambda_innerClass.lambda;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class lambdaTest {
public static void main(String[] args) {
var planets = new String[]{"Mercury", "Venus", "Earth", "Mars",
"Jupiter", "Saturn", "Uranus", "Neptune"};
System.out.println(Arrays.toString(planets));
System.out.println("Sorted in dictionary order:");
Arrays.sort(planets);
System.out.println(Arrays.toString(planets));
System.out.println("Sorted by length:");
Arrays.sort(planets, (first, second) -> first.length() - second.length());//第二个参数应该是比较器
System.out.println(Arrays.toString(planets));
var timer = new Timer(1000, event -> System.out.println("The time is " + new Date()));
timer.start();
JOptionPane.showMessageDialog(null, "quit?");
System.exit(0);
}
}
函数式接口
: 对于只有一个抽象方法的接口,需要这种接口对象时, 可以提供一个lambda表达式lambda
表达式可以转换为接口var timer = new Timer(1000, event->
{
System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));
Toolkit.getDefaultToolkit().beep();
})
ArrayList
类的removeIf
方法参数是Predicate
public interface Predicate<T>
{
boolean test(T t);
}
//删除列表所有null值
list.removeIf(e -> e == null);
懒
计算public interface Supplier<T>
{
T get();
}
LocalDate hireDay = Objects.requireNonNullOrElse(day, new LocalDate(1970, 1, 1));
//通过使用供应者supplier, 延迟该计算 , 只有day==null时候才调用供应者(构造默认的LocalDate)
LocalDate hireDay = Objects.requireNonNullOrElse(day, () -> new LocalDate(1970, 1, 1));
var timer = new Timer(1000, event->System.out.println(event));
var timer = new Timer(1000, System.out::println);
System.out::println
是一个方法引用, 它指示编译器生成一个函数式接口的实例,覆盖这个接口的抽象方法来调用给定的方法
上面的例子, 会生成一个ActionListener
, 他的actionPerformed(ActionEvent e)
方法要调用System.out.println(e)
方法引用不是对象, 为一个类型为函数式接口的变量赋值时会生成一个对象
方法引用示例与等价的lambda表达式见P248
当lambda表达式的体只调用一个方法而不做其他操作的时候才能把方法引用重写为方法引用
Person::new
就是Person
构造器的一个引用int[]::new
是一个构造器引用, 他有一个参数,即数组的长度, 等价于x->new int[x]
lambda
表达式lambda表达式重点是延迟执行
Runnable
作为无参数或返回值的动作运行, action.run()
会调用lambda表达式主体
package test;
import java.util.function.IntConsumer;
public class lambda {
public static void main(String[] args)
{
repeat(10, ()->System.out.println("hello, world"));
repeat(10, (i)->System.out.println("Countdown:" + (9-i)));
}
public static void repeat(int n, Runnable action)
{
for(int i = 0; i < n; i++) action.run();
}
public static void repeat(int n, IntConsumer action) {
for(int i = 0; i < n; i++)action.accept(i);
}
}
Comparator
类以下这个例子生成的无参数构造器如下
public TimePrinter(TalkingClock clock){outer = clock;}
package chapter6_interface_lambda_innerClass.innerClass;
import java.awt.*;
import java.awt.event.*;
import java.time.*;
import javax.swing.*;
public class innerClassTest {
public static void main(String[] args) {
var clock = new TalkingClock(1000, true);
clock.start();
JOptionPane.showMessageDialog(null, "Quit?");
System.exit(0);
}
}
class TalkingClock{
private int interval;
private boolean beep;
public TalkingClock(int interval, boolean beep) {
this.interval = interval;
this.beep = beep;
}
public void start() {
var listener = new TimerPrinter();
var timer = new Timer(interval, listener);
timer.start();
}
public class TimerPrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
}
}
}
TimePrinter
名字只出现了一次,在start
方法中创建这个类型对象时使用了一次.可以在一个方法中局部定义这个类
public void start() {
class TimerPrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
}
}
var listener = new TimerPrinter();
var timer = new Timer(interval, listener);
timer.start();
}
局部内部类声明时候不能有访问修饰符public, private
优势: 对外部完全隐藏,除了start
代码
局部内部类不仅能够访问外部类字段,还可以访问局部变量(事实最终变量)
public void start(int interval, boolean beep) {
class TimerPrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
}
}
var listener = new TimerPrinter();
var timer = new Timer(interval, listener);
timer.start();
}
匿名内部类不需要为类指定名字
以下代码: 创建了一个类的新对象,这个类实现了ActionListener
接口, 需要实现的方法{}中定义
public void start(int interval, boolean beep) {
var listener = new ActionListener();
{
public void actionPerformed(ActionEvent event) {
System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
}
};
var timer = new Timer(interval, listener);
timer.start();
}
//用lambda表达式
public void start(int interval boolean beep)
{
var timer = new Timer(interval, event->{
System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));
if (beep) Toolkit.getDefaultToolkit().beep();
})
}
语法如下
new SuperType(construction parameters)
{
innner class methods and data
}
SuperType
可以是接口,内部类就要实现这个接口;如果是一个类,内部类就要扩展这个类
vae queen = new Person("Marry");
var count = new Person("bjcs"){...};
匿名内部类不能有构造器但是可以提供一个对象的
双括号初始化:
var f = new ArrayList<String>();
f.add("Harry");
f.add("Alice");
invite(f);
--->
invite(new ArrayList<String>)(){{add("Harry"); add("Alice");}}
//外层括号建立了一个匿名子类,内层括号是一个初始化块
得到匿名内部类的外部类类名不能直接getClass
,这个方法带调用this.getClass()
, 静态方法没有隐式参数
new Object(){}.getClass().getEnclosingClass()
new Object()
建立Object
的匿名子类的一个匿名对象,getEnclosingClass
则得到其外围类,也就是包含这个静态方法的类
public, static
package chapter6_interface_lambda_innerClass.staticInnerClass;
public class StaticInnerClassTest
{
public static void main(String[] args)
{
var values = new double[20];
for (int i = 0; i < values.length; i++)
values[i] = 100 * Math.random();
ArrayAlg.Pair p = ArrayAlg.minmax(values);
System.out.println("min = " + p.getFirst());
System.out.println("max = " + p.getSecond());
}
}
class ArrayAlg{
public static class Pair {
/* 一个静态的内部类 */
private double first;
private double second;
/**
* Constructs a pair from two floating-point numbers
*
* @param f the first number
* @param s the second number
*/
public Pair(double f, double s) {
first = f;
second = s;
}
/**
* Returns the first number of the pair
*
* @return the first number
*/
public double getFirst() {
return first;
}
/**
* Returns the second number of the pair
*
* @return the second number
*/
public double getSecond() {
return second;
}
}
/**
* Computes both the minimum and the maximum of an array
* @param values an array of floating-point numbers
* @return a pair whose first element is the minimum and whose second element
* is the maximum
*/
public static Pair minmax(double[] values)
{
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (double v : values) {
if (min > v) {
min = v;
}
if (max < v) {
max = v;
}
}
return new Pair(min, max);
}
}
Object
类中的全部方法(equals, toStirng
等)Proxy
类的newProxyInstance
方法, 有三个参数
Class
对象数组,每个元素对应需要实现的各个接口package chapter6_interface_lambda_innerClass.proxy;
import java.lang.reflect.*;
import java.util.*;
public class ProxyTest
{
public static void main(String[] args) {
var elements = new Object[1000];
for (int i = 0; i < elements.length; i++) {
Integer value = i + 1;
var handler = new TraceHandler(value);
Object proxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{Comparable.class}, handler);
elements[i] = proxy;
}
Integer key = new Random().nextInt(elements.length) + 1;
int result = Arrays.binarySearch(elements, key);
if(result >= 0) System.out.println(elements[result]);
}
}
class TraceHandler implements InvocationHandler {//打应输出方法名和参数,并且调用该方法
private Object target;
public TraceHandler(Object t) {
target = t;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
System.out.print(target);
System.out.print("." + m.getName() + "(");
if (args != null) {
for (int i = 0; i < args.length; i++) {
System.out.print(args[i]);
if(i < args.length - 1) System.out.print(", ");
}
}
System.out.println(")");
return m.invoke(target, args);
}
}
Proxy
, 一个代理类只有一个实例字段即调用处理器,在超类Proxy
中定义toString, hasCode, equals
方法, 这些方法只是在调用处理器上调用invoke
.