杂项
1.接口
// 一个示例。接口中的所有方法都自动是public方法。
// 最新的Comparable支持泛型,可以避免装拆箱
public interface Comparable
{
int compareTo(Object other);
}
class Employee implements Comparable
{
public int compareTo(Object otherObject)
{
Employee other = (Employee)otherObject;
return Double.compare(salary, other.salary);
}
}
接口不会有实例字段。
接口可包含常量。
接口中的字段总是public static final。
2.继承
java中每个类只能有一个超类,但却可以实现多个接口。
class Employee implements Cloneable, Comparable
接口使用上类似抽象基类,但绕开了超类只能有一个的限制。
2.静态和私有方法
接口支持静态方法,私有方法。
3.默认方法
可以为接口方法提供一个默认实现,必须用default修饰符标记这样的一个方法。
public interface Comparable
{
default int compareTo(T other)
{
return 0;
}
}
4.类优先
类A派生自类B,派生自接口C。
B,C包含签名一致方法,类A中采用B的。
5.接口与回调
6.常用接口示例
public interface Comparator
{
int compare(T first, T second);
}
class LengthComparator implements Comparator
{
public int compare(String first, String second)
{
return first.length() - second.length();
}
}
String[] friends = {"Peter", "Paul", "Mary"};
Arrays.sort(friends, new LengthComparator());
Object提供了clone方法的默认实现,Cloneable接口是java提供的少数标记接口之一。
Comparable等接口的通常用途是确保一个类实现一个或一组特定的方法。标记接口不包含任何方法,
它唯一的作用就是允许在类型查询中使用instanceof。
if(obj instanceof Cloneable)
// 依旧浅拷贝
class Employee implements Cloneable
{
public Employee clone() throws CloneNotSupportedException
{
return (Employee)super.clone();
}
...
}
// 深拷贝
class Employee implements Cloneable
{
...
public Employee clone() throws CloneNotSupportedException
{
Employee cloned = (Employee)super.clone();
cloned.hireDay = (Date)hireDay.clone();
return cloned;
}
}
如果在一个对象上调clone,但此对象的类没实现Cloneable接口,
Object类的clone方法会抛出一个CloneNotSupportedException。
lambda表达式
lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。
lambda表达式就是一个代码块,及必须传入代码的变量规范。
// 一个lambda示例
(String first, String second) ->
{
if(first.length() < second.length())
{
return -1;
}
else if(first.length() > second.length())
{
return 1;
}
else
{
return 0;
}
}
lambda表达式的返回类型总是会由上下文推导得出。
- 函数式接口
前面讨论过,java中有很多封装代码块的接口。
lambda表达式与这些接口是兼容的。
对于只有一个抽象方法的接口,需要这种接口的对象时,可以提供一个lambda表达式。
这种接口称为函数式接口。
java没有函数类型,lambda表达式不可赋值给Object变量。
java api在java.util.function包中定义了很多非常通用的函数式接口。
其中一个接口BiFunction描述了参数类型为T和U且返回类型为R的函数。
BiFunction comp = (first, second)->first.length() - second.length();
想要用lambda表达式做某些处理,要谨记表达式的用途,为它建立一个特定的函数式接口。
public interface Predicate
{
boolean test(T t);
}
public interface Supplier
{
T get();
}
- 方法引用
var timer = new Timer(1000, System.out::println);
表达式System.out::println是一个方法引用,它指示编译器生成一个函数式接口的实例。
覆盖这个接口的抽象方法来调用给定的方法。
在这个例子中,会生成一个ActionListener,
它的actionPerformed(ActionEvent e)方法要调用System.out.println(e)。
Arrays.sort(strings, String::compareToIgnoreCase);
要用::运算符分隔方法名与对象或类名,主要有三种情况:
a.object::instanceMethod,
对于System.out::println,等价于x->System.out.println(x)
b.Class::instanceMethod
String::compareToIgnoreCase等同于(x, y)->x.compareToIgnoreCase(y)
c.Class::staticMethod
Math::pow等价于(x, y)->Math.pow(x, y)
separator::equals x->separator.equals(x)
String::trim x->x.trim()
String::concat (x, y)->x.concat(y)
Integer::valueOf x->Integer::valueOf(x)
Integer::sum (x, y)->Integer::sum(x, y)
Integer::new x->new Integer(x)
Integer[]::new n->new Integer[n]
只有当lambda表达式的体只调用一个方法而不做其他操作时,才能把lambda表达式重写为方法引用。
// 这里有一个方法调用和一个比较,故不能用方法引用。
s->s.length() == 0
可以在方法引用中使用this,如
this::equals等同于x->this.equals(x)
使用super也是合法的,
- 构造器引用
构造器引用与方法引用类似
Integer::new x->new Integer(x)
Integer[]::new n->new Integer[n]
- 变量作用域
lambda表达式有3个部分
a.一个代码块
b.参数
c.自由变量的值,这是指非参数而且不在代码中定义的变量。
表示lambda表达式的数据结构必须存储自由变量的值。
代码块及自由变量有一个术语:闭包。
lambda表达式中捕获的变量必须实际上是事实最终变量。
事实最终变量是指,这个变量初始化之后就不会再为它赋新值。
lambda表达式的体与嵌套块有相同的作用域。
这里同样适用命名冲突和遮蔽的有关规则。
在lambda表达式中声明与一个局部变量同名的参数或局部变量是不合法的。
一个方法中,不能有两个同名的局部变量。lambda表达式中同样也不能有同名的局部变量。
- 处理lambda表达式
1.生成lambda表达式
2.lambda表达式传递到需要一个函数式接口的方法
3.如何编写方法处理lambda表达式
使用lambda表达式的重点是延迟执行。
常用函数式接口
函数式接口 参数类型 返回类型 抽象方法名
Runnable 无 void run
Supplier 无 T get
Consumer T void accept
BiConsumer T,U void accept
Function T R apply
BiFunction T,U R apply
UnaryOperator T T apply
BinaryOperator T,T T apply
Predicate T boolean test
BiPredicate T,U boolean test
public static void repeat(int n, Runnable action)
{
// 若参数2为lambda表达式,调action.run()会执行lambda表达式的主体。
for(int i = 0; i < n; i++) action.run();
}
repeat(10, ()->System.out.println("Hello"));
public interface IntConsumer
{
void accept(int value);
}
public static vodi repeat(int n, IntConsumer action)
{
for(int i = 0; i < n; i++) action.accept(i);
}
repeat(10, i->System.out.println("Countdown: " + (9 - i)));