在 Java 程序设计语言中, 接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。
接口中的所有方法自动地属于 public。 因此,在接口中声明方法时,不必提供关键字 public ,在实现接口时, 必须把方法声明为 public.
为了让类实现一个接口, 通常需要下面两个步骤:
使用Comparable接口为对象排序, Arrays类中的 sort 方法承诺可以对对象数组进行排序,但要求满 足下列前提:对象所属的类必须实现了 Comparable 接口
public class Test {
public static void main(String[] args) throws IOException {
People[] arr = new People[5];
arr[0] = new People("张三",18);
arr[1] = new People("李四",8);
arr[2] = new People("大白",9);
arr[3] = new People("小白",34);
arr[4] = new People("零",24);
Arrays.sort(arr);
for(People people: arr){
System.out.println(people.toString());
}
}
}
///
public class People implements Comparable<People> {
private String name;
private int age;
public People() {
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(People o) {
//如果 x < y 返回一个负整数;如果 x 和 y 相等,则返回 0; 否则返回一个负整数。
return Integer.compare(age, o.getAge());
}
}
x = new Comparable(. . .); // ERROR
Comparable x; // OK
if (anObject instanceof Comparable) { . . . }
public interface Moveable {
void move(double x, double y);
}
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
public interface Powered extends Moveable{
double milesPerCallon();
// 接口中的域将被自动设为 public static final
double SPEED_LIHIT = 95; // a public static final constant
}
public static final
public interface Moveable {
void move(double x, double y);
// 静态方法
static int add(int a,int b){
return a+b;
}
}
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// 直接类名调用就行
int add = Moveable.add(1, 2);
System.out.println(add);
default
修饰符标记这样一个方法。public interface Moveable {
void move(double x, double y);
int NUM = 5;
// 默认方法
default int add(int a,int b){
return a+b+NUM;
}
}
// 在实现类中覆盖 或者不覆盖,直接对象调用,但最好别这样做.
@Override
public int add(int a, int b) {
return (a+b+NUM)*2;
}
接口演化
;我们要为以前的接口类再添加一个方法,则实现该接口的所有类都必须实现该方法,那么我们要修改源代码,非常麻烦,如果先在一个接口中将一个方法定义为默认方法, 然后又在超类或另一个接口中定义了 同样的方法, 会发生什么情况? 诸如 Scala 和 C++ 等语言对于解决这种二义性有一些复杂的 规则。幸运的是,Java 的相应规则要简单得多。规则如下:
如果两个接口都有相同的默认方法,我们可以在实现类中重写这个方法,就不会产生冲突了.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
public class TimePrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone, the time is " + new Date());
// static Toolkit getDefaultToolkit() : 获得默认的工具箱。T.具箱包含有关 GUI 环境的信息。
// void beep() 发出一声铃响。
Toolkit.getDefaultToolkit().beep();
}
}
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
import javax.swing.*;
import java.awt.event.ActionListener;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
ActionListener listener = new TimePrinter();
//构造一个定时器, 每隔 interval 毫秒通告 listener—次,
Timer timer = new Timer(10000,listener);
//启动定时器一旦启动成功, 定时器将调用监听器的 actionPerformed。
timer.start();
//显示一个包含一条消息和 OK 按钮的对话框。这个对话框将位于其 parent组件的中 央。如果 parent 为 mill, 对话框将显示在屏幕的中央,
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
import java.util.Comparator;
/**
* 按长度比较字符串,可以如下定义一个实现 Comparator 的类
*/
public class LengthComparator implements Comparator<String> {
@Override
public int compare(String first, String second) {
return first.length() - second.length();
}
}
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
import java.io.IOException;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws IOException {
String[] words = {
"aaa","zhangsan","vvvv","d"};
LengthComparator comparator = new LengthComparator();
Arrays.sort(words,comparator);
for(String word: words){
System.out.println(word);
}
}
}
Employee original = new Employee("John Public", 50000);
Employee copy = original;
copy.raiseSalary(lO); // oops-also changed original
0 -> { for (inti = 100;i >= 0;i ) System.out.println(i); }
Comparator<String> comp
= (first,second) // Same as (String first, String second)
-> first.length() - second.length();
ActionListener listener = event ->
System.out.println("The time is " + new Date()");
// Instead of (event) -> . .. or (ActionEvent event) -> ...
(String first, String second) -> first.length() - second.length()
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
// 抽象方法
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
\
list.removelf(e -> e == null);
Timer t = new Timer(1000, event -> System.out.println(event)):
Timer t = new Timer(1000, System.out::println);
System.out::println
是一个方法引用(method reference), 它等价于 lambda 表达式x 一> System.out.println(x)
Arrays.sort(strings,String::conpareToIgnoreCase)
super::instanceMethod
做一个总结: 在lambda中,不能改变代码块之外的变量值,也不能使用代码块之外会改变值的变量.
通常, 你可能希望能够在 lambda 表达式中访问外围方法或类中的变量。考虑下面这个 例子:
public static void repeatMessage(String text, int delay)
{
ActionListener listener = event ->
{
System.out.println(text);
Toolkit.getDefaultToolkitO.beep();
};
new Timer(delay, listener).start();
}
repeatMessage("Hello", 1000); // Prints Hello every 1 ,000 milliseconds
在我们的例子中, 这个 lambda 表达式有 1 个自由变量 text。表示 lambda 表达式的数据 结构必须存储自由变量的值,在这里就是字符串 “Hello”。我们说它被 lambda 表达式捕获
可以看到,lambda 表达式可以捕获外围作用域中变量的值。在 Java 中, 要确保所捕获 的值是明确定义的,这里有一个重要的限制。在 lambda 表达式中, 只能引用值不会改变的 变量
如果设计你自己的接口,其中只有一个抽象方法, 可以用 @FunctionalInterface 注 解来标记这个接口。这样做有两个优点。如果你无意中增加了另一个非抽象方法, 编译 器会产生一个错误消息。 另外javadoc 页里会指出你的接口是一个函数式接口。 并不是必须使用注解根据定义,任何有一个抽象方法的接口都是函数式接口。不 过使用 @FunctionalInterface 注解确实是一个很好的做法。
最好使用表 6-1 或表 6-2 中的接口。 例如, 假设要编写一个方法来处理满足 某个特定条件的文件。 对此有一个遗留接口java.io.FileFilter, 不过最好使用标准的 Predicate , 只有一种情况下可以不这么做, 那就是你已经有很多有用的方法可以生 成 FileFilter 实例。
Comparator 接口包含很多方便的静态方法来创建比较器。这些方法可以用于 lambda 表 达式或方法引用。
静态 comparing 方法取一个“ 键提取器” 函数, 它将类型 T 映射为一个可比较的类型 (如 String)。对要比较的对象应用这个函数, 然后对返回的键完成比较。例如,假设有一个 Person 对象数组,可以如下按名字对这些对象排序:
Arrays.sort(people, Comparator.comparing(Person::getName));
与手动实现一个 Compamtor 相比, 这当然要容易得多。另外, 代码也更为清晰, 因为显 然我们都希望按人名来进行比较。
Arrays.sort(arr,
Comparator.comparing(People::getName,
(s, t) -> Integer.compare(s.length(), t.length()))
.thenComparing(People::getName)
.thenComparing(People::getAge)
);
另外,comparing 和 thenComparing 方法都有变体形式,可以避免 int、 long 或 double 值 的装箱。要完成前一个操作,还有一种更容易的做法:
Arrays.sort(arr, Comparator.comparinglnt(p -> p.getName-length()));
public class Test {
public static void main(String[] args) throws IOException {
// 对象数组
People[] arr ={
new People("zhangsan",12),
new People("lisi",17),
new People("lisi",15),
new People("wangwu",12),
new People("lisi",78),
new People("dabai",12),
new People("haha",30),
new People("lisi",1),};
// 按照名字对对象排序
Arrays.sort(arr,Comparator.comparing(People::getName));
//System.out.println( Arrays.toString(arr));
for (People p : arr){
System.out.println(p.toString());
}
System.out.println("----------------------------------");
// 如果名字相同,可以追加一个(多个)比较器,按照年龄排序
Arrays.sort(arr, Comparator.comparing(People::getName).thenComparing(People::getAge));
for (People p : arr){
System.out.println(p.toString());
}
System.out.println("----------------------------------");
// 可以为 comparing 和 thenComparing 方法提取的键指定一个 比较器。
// 例如,可以如下根据人名长度完成排序,
// 然后长度相同的按照名字排序.
// 名字在相同的按照年龄排序.
/* Arrays.sort(arr,
Comparator.comparing(People::getName,
(s, t) -> Integer.compare(s.length(), t.length()))
.thenComparing(People::getName)
.thenComparing(People::getAge)
);*/
// 按照人名长度完成排序的另一种写法
Arrays.sort(arr,Comparator.comparingInt(p->p.getName().length()));
for (People p : arr){
System.out.println(p.toString());
}
}
}