网站首页 > java教程 正文
原文
第6章 接口、lambda 表达式与内部类
- 接口
- lambda表达式
- 内部类
- 服务加载器
- 代理
到目前为止,你已经学习了 Java 中面向对象编程的核心概念: 类和继承。本章将介绍几种常用的高级技术。尽管这些内容可能不太容易理解,但一定要掌握,以完善你的 Java工具箱。
?首先介绍第一种技术,即接口 (interface),接口用来描述类应该做什么,而不指定它们具体应该如何做。一个类可以实现 (implement)一个或多个接口。只要符合所要求的接口,就可以使用实现了这个接口的类(即实现类)的对象。讨论接口以后,我们会继续介绍lambda 表达式,这是一种简洁的方法,用来创建可以在将来某个时间点执行的代码块。通过使用lambda 表达式,可以用一种精巧而简洁的方式表示使用回调或可变行为的代码。
?接下来,我们将讨论内部类 (inner class) 机制。理论上讲,内部类有些复杂,内部类定义在另外一个类的内部,它们的方法可以访问其外部类的字段。内部类技术在设计合作类集合时很有用。
?在本章的最后还将介绍代理(proxy),这是实现任意接口的对象。代理是一种非常专业的构造,可以用来构建系统级的工具。如果是第一次阅读本书,可以先跳过那一节。
6.1 接口
?在下面的小节中,你会了解 Java 接口是什么以及如何使用,另外还会了解 Java 最新的几个版本中接口的功能有怎样的提升。
6.1.1接口的概念
?在Java 程序设计语言中,接口不是类,而是对希望符合这个接口的类的一组需求。我们经常听到某个服务的提供商这样说:“如果你的类符合某个特定接口,我就会履行这项服务。”下面给出一个具体的示例。Arrays类中的 sort 方法承诺可以对对象数组进行排序,但要求满足下面这个条件:对象所属的类必须实现 Comparable 接口。
下面是 Comparable 接口的代码:
public interface Comparable{
int compareTo(Object other);
}
在这个接口中,compareTo方法是抽象的,它没有具休实现。任何实现 Comparable 接口的类都需要包含一个 compareTo 方法,这个方法必须接受一个 Object 参数,并返回一个整数。否则、这个类也应当是抽象的,也就是说,你不能构造这个类的对象。
?
注释:在Java5中,Comparable接口已经提升为一个泛型类型
public interface Comparable<T>
{
int compareTo(T other): // parameter has type T
}
例如、在实现 Comparable<Employee>接口的类中,必须提供以下方法
int compareTo<Employee other)
仍然可以使用不带类型参数的“原始”Comparable类型。这样一来,compareTo 方法就有一个 Object 类型的参数,你必须手动将 compareTo 方法的这个参数强制转换为所希望的类型。稍后我们再做这个工作,所以不用担心同时学习两个新概念。
?接口中的所有方法都自动是 public 方法。因此,在接口中声明方法时,不必提供关键字public。
?当然,还有一个接口没有明确说明的额外要求:调用x.compareTo(y) 的时候,这个 compareTo方法实际上必须能够比较两个对象,并返回比较的结果,即x和y哪一个更大。当x小于y时,返回一个负数;当x等于y时,返回0;否则返回一个正数。
?这个特定接口只有一个方法,而有些接口可能包含多个方法。稍后可以看到,接口还可以定义常量。不过,更重要的是要知道接口不能提供什么。接口绝不会有实例字段,在 Java8之前,接口中的方法都是抽象方法。(在 6.1.4节和6.1.5节中可以看到,现在接口中还可以有其他方法。当然,那些方法不能引用实例字段一一接口没有实例。)
?
?现在,假设希望使用Arrays类的 sort 方法对 Employee 对象数组进行排序,Employee 类就必须实现Comparable 接口。
?为了让类实现一个接口,需要完成下面两个步骤:
1.将类声明为实现给定的接口。
2.对接口中的所有方法提供定义。
?要声明一个类实现某个接口,需要使用关键字 implements:
class Employee implements Comparable
?当然,现在 Employee类需要提供 compareTo 方法。假设我们希望根据员工的薪水进行比较。以下是 compareTo 方法的一个实现:
public int compareTo(Object otherObject){
Employee other = (Employee) otherObject;
return Double.compare(salary, other.salary);
}
在这里,我们使用了静态 Double.compare 方法。如果第一个参数小于第二个参数,它会返回一个负值;如果二者相等则返回 0; 否则返回一个正值。
警告:在接口声明中,没有将 compareTo 方法声明为 public,这是因为,接口中的所有方法都自动是public方法。不过,在实现接口时,必须把方法声明为 public;否则,编译器将认为这个方法的访问属性是包可访问,这是类中默认的访问属性,之后编译器就会报错,指出你试图提供更严格的访问权限。
?我们可以做得更好一些。可以为泛型 Comparable 接口提供一个类型参数
class Employee implements Comparable<Employee>{
public int compareTo(Employee other){
return Double.compare(salary,other.salary);
}
}
请注意,对 Object 参数进行强制类型转换总是让人感觉不太顺眼,但现在已经不见了。
?提示:Comparable 接口中的 compareTo 方法将返回一个整数。如果两个对象不相等,返回哪个正值或者负值并不重要。在对两个整数字段进行比较时,这种灵活性非常有用。例如,假设每个员工都有一个唯一的整数 id,你希望根据员工ID号进行排序,那么可以直接返回 idother,id。如果第一个ID号小于另一个ID,这个值将是一个负值;如果两个ID相等,这个值就是 0;否则,这将是一个正值。但有一点需要注意:整数的范围要足够小,以避免减法运算溢出。如果能够确信 ID 为非负数,或者它们的绝对值不会超过(Integer,MAX VALUE-1)/2,就不会出现问题。否则,可以调用静态Integer.compare 方法。
当然,这里的相减技巧不适用于浮点数。因为如果 salary和other,salary 很接近但又不相等,它们的差经过四舍五人后有可能变成 0。如果x<y,Double.compare(x,y)调用会返回 -1;如果x>y则返回1。
注释: Comparable接口的文档建议 compareTo 方法应当与 equals 方法兼容。也就是说,当x.equals(y)时 x.compareTo(y)就应当等于0。Java API中大多数实现 Comparable接口的类都遵从了这个建议。不过有一个重要的例外,就是 BigDecimal。考虑x= new BigDecimal("1.0")和y=new BigDecimal("1.00")。这里xequals(y)为false,因为两个数的精度不同。不过x.compareTo(y)为0。理想情况下应该不返回0,但是没有明确的方法能够确定这两个数哪一个更大。
?现在,我们已经看到,要让一个类使用排序服务必须让它实现 compareTo 方法。这是理所当然的,因为要向 sort 方法提供对象的比较方式。但是为什么不能在 employee 类中直接提供
个compareTo方法(而不实现 omparable 接口)呢?使用接口的主要原因在于: Java 程序设计语言是一种强类型 (strongly typed)语言。调用方法时,编译器要能检查这个方法确实存在。在 sort 方法中可能会有类似下面的语句:
if(a[i].compareTo(a[j]) > 0{
// rearrange a[i] and a[j]
// ...
}
编译器必须确认 a[i]一定有一个 compareTo方法。如果a是一个 Comparable 对象的数组就可以确保肯定有 compareTo方法,因为每个实现 Comparable 接口的类都必须提供这个方法。
注释:你可能认为,如果将Arrays 类中的 sort 方法定义为接受一个 Comparable[] 数组, 倘若有人调用 sort 方法时所提供数组的元素类型没有实现 Comparable 接口,编译器就能报错。遗憾的是,事实并非如此。实际上,sort 方法接受一个 Object[]数组,并使用一个笨拙的强制类型转换:
// approach used in the standard library--not recommended
if (((Comparable) a[i]).compareTo(a[j])>0)
// rearrange ali] and alj)
如果a[i] 不属于一个实现了 Comparable 接口的类,虚拟机就会抛出一个异常。
程序清单 6-1 给出了对 Employee 类(见程序清单6-2)实例数组进行排序的完整代码
程序清单6-1 interfaces/EmployeeSortTest.java
package interfaces;
import java.util.*;
/**
* This program demonstrates the use of the Comparable interface.
* @version 1.30 2004-02-27
* @author Cay Horstmann
*/
public class EmployeeSortTest
{
public static void main(String[] args)
{
var staff = new Employee[3];
staff[0] = new Employee("Harry Hacker", 35000);
staff[1] = new Employee("Carl Cracker", 75000);
staff[2] = new Employee("Tony Tester", 38000);
Arrays.sort(staff);
// print out information about all Employee objects
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
}
}
程序清单6-2 interfaces/Employee.java
package 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 String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
/**
* Compares employees by salary
* @param other another Employee object
* @return a negative value if this employee has a lower salary than
* otherObject, 0 if the salaries are the same, a positive value otherwise
*/
public int compareTo(Employee other)
{
return Double.compare(salary, other.salary);
}
}
API java.lang.Comparable<T> 1.0
- int compareTo(T other) 对这个对象与 other 进行比较。如果这个对象小于 other 则返回一个负整数;如果二者相等则返回 0:否则返回一个正整数。
API java.util.Arrays 1.2
- static void sort(Object[] a) 对数组 a 中的元素进行排序。要求数组中的元素必须属于实现了 Comparable 接口的类, 并且元素之间必须是可比较的。
API java.lang.Integer 1.0
- static int compare(int x,int y) 7 如果 x<y 返回一个负整数;如果x和y 相等,则返;否则返回一个正整数。
API java.lang.Double 1.0
- static int compare (double x,double y) 1.4 如果 x<y 返回一个负整数;如果x和y 相等,则返;否则返回一个正整数。
注释:语言标准规定:“对于任意的x和y,实现者必须确保 sgn(x.compareTo(y)) = -sgn(y.compareTo(x))。(也就是说,如果y.compareTo(x)抛出一个异常,那么x.compareTo(y)也应该抛出一个异常。)”这里的 sgn 是一个数的符号:如果n是负值,sgn(n)为 -1; 如果n等于0,sgn(n)为0;如果n是正值,sgn(n)为1。简单地讲,如果翻转 compareTo的参数,结果的符号也应该翻转(但具体值不一定)。
?与 equals 方法一样,使用继承时有可能会出现问题。
?这是因为Manager 扩展了 Employee,而 Employee 实现了 Comparable<Employee>,而不是Comparable<Manager>。如果 Manager 要盖 compareTo,就必须做好准备比较经理与员工,绝不能简单地将员工强制转换成经理:
class Manager extends Employee{
public int compareTo(Employee other){
Manager otherManager = (Manager) other; // NO
}
}
? ?违反了“反对称”规则。如果x是一个 Employee 对象,y是一个Manager 对象,调用x.compareTo(y)不会抛出异常,它只是将x和y都作为员工进行比较。但是反过来y.compareTo(x)将会抛出一个 ClassCastException。
?这种情况与第5章中讨论的 equals 方法一样,补救方式也一样。有两种不同的情况。
?如果不同子类中的比较有不同的含义,就应该将属于不同类的对象之间的比较视为非法。每个 compareTo 方法首先都应该进行以下检测:
if (getClass() != other.getClass()) throw new ClassCastException();
?如果存在一个比较子类对象的通用算法,那么只需要在超类中提供一个 compareTo方法,并将这个方法声明为 final。
?例如,假设你希望经理大于普通员工,而不论薪水多少,那么诸如Executive和Secretary 等其他子类呢? 如果要按照职位排序,那就应该在 Employee 类中提供一个 rank方法。让每个子类覆盖 rank,并实现一个考虑 rank值的 compareTo 方法。
猜你喜欢
- 2024-09-14 Java核心技术梳理-类加载机制与反射
- 2024-09-14 JAVA核心技术基础部分的总结(java核心技术基础部分的总结)
- 2024-09-14 「Spring专场」不看源码就带你认识核心流程以及运作原理
- 2024-09-14 Java核心技术梳理-泛型(java泛型的特点)
- 2024-09-14 Spring的核心模块解析(spring的核心技术包含在哪个模块中)
- 2024-09-14 java技术专家带你深入java核心技术:反射+继承设计技巧+枚举类
- 2024-09-14 Java EE的13种核心技术(java核心技术卷一)
- 2024-09-14 做了这么多年的JAVA开发,JVM的核心技术现在看看也不晚
- 2024-09-14 Java核心技术,继承:类+继承层次+强制类型转换+多态+抽象类
- 2024-09-14 一文带你深入Java核心技术:对象克隆+接口与回调,还有这种操作
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)