CoreJava读书笔记--接口、lambda表达式与内部类(一)--接口

接口

(一)接口概念

在Java中,接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的格式进行定义。

如果类遵从某个特定的接口,那么就履行这项服务。例如:Arrays类中的sort方法承诺可以对对象数组进行排序,但是要满足一个前提:对象所属的类必须实现了Comparable接口。下面是Comparable接口的代码:

public interface Comparable{
  int compareTo(Object obj);
}

这就是说任何实现Comparable接口的类都需要包含compareTo方法,并且这个方法的参数必须是Object对象,返回一个整型数值。

接口的注意事项:

①接口中所有的方法自动public,所以在接口声明方法时,不必提供关键字public

② 接口可以包含很多方法,不止一个

③接口中可以定义常量

④接口绝不能含有实例域

⑤在Java8之前,也不能在接口中实现方法,但是现在可以在接口中提供简单方法(当然这些方法不能引用实例域)

现在,假设我们要使用Arrays类中的sort方法对Employee对象数组进行排序,那么Employee类就必须实现Comparable接口,通常需要以下两个步骤:

①将类声明为实现给定的接口

②对接口中的所有方法进行定义

class Employee implements Comparable{
    ...
    public int compareTo(Object anotherObject){
        Employee other = (Employee)otherObject;
        return Double.compare(salary,other.salary);//假设按薪水排序,如果第一个参数小于第二个参数就会返回一个负值,相等则返回0,否则返回一个正值
    }
}

当然我们也可以为泛型Comparable接口提供一个类型参数,使程序看起来更舒服。示例:

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 void setName(String name) {
		this.name = name;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}
	
	//提高薪酬
	public void raiseSalary(double byPercent) {
		double raise = salary*byPercent/100;
		salary+=raise;
	}
	
	/**
	 * 实现接口
	 * 按薪酬排序
	 */
	public int compareTo(Employee other) {
		return Double.compare(salary, other.salary);
	}
}
package interfaces;

import java.util.Arrays;

/**
 * This program demonstrates the use of the Comparable interface.
 * @author 弓长小月
 *
 */
public class EmployeeSortTest {
	public static void main(String[] args) {
		Employee[] 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);
		
		for(Employee e : staff) {
			System.out.println("name="+e.getName()+",salary="+e.getSalary());
		}
	}
}

 注释:与equals方法一样,在继承过程中有可能会出现问题,所以在每个compareTo方法都应该在开始时进行检测:

if(getClass()!=other.getClass()) throw new ClassCastException();

(二)接口的特性

总结以下六点:

①不能使用new关键字

②接口能声明变量,但必须引用实现了接口的类对象

③可以使用instance检查一个对象是否实现了某个特定的接口,如同使用instanceof检查一个对象是否属于某个特定类一样

④接口可以被继承

⑤接口中不能有实例域和静态方法,但却可以包含常量

⑥尽管每个类只能有一个超类,但却可以实现多个接口

(三)接口与抽象类

为什么不将Comparable直接设计成抽象类呢?

因为每个类只能扩展于一个类,但每个类可以实现多个接口,Java不支持多继承,但是接口可以提供多重继承带来的好处,同时也能避免多重继承的复杂性和低效性。

(四)静态方法

在Java8中,允许在接口中增加静态方法。通常的做法都是将静态方法放在伴随类中,例如:在标准库中,我们会看到成对出现的接口和实用工具类,Collection/Collections或Path/Paths。

(五)默认方法

在Java8中,可以为接口提供一个默认实现,但必须用default修饰符标记。

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

当某个类实现Comparable接口时,可以不用实现compareTo方法,或者有选择性的实现该方法。

默认方法主要优势是提供了一种扩展接口的方法,而不破坏现有代码。如果一个已经投入使用的接口需要扩展一个新的方法,在JDK8以前,我们必须再该接口的所有实现类中都添加该方法的实现,否则编译会出错。如果实现类数量很少且我们有修改的权限,可能工作量会少,但是如果实现类很多或者我们没有修改代码的权限,这样的话就很难解决了。而默认方法提供了一个实现,当没有显式提供时就默认采用这个实现,这样新添加的接口就不会破坏现有的代码。

默认方法另一个优势是该方法是可选的,子类可以根据不同的需求经override实现或者采用默认实现。下面看一个例子:

package interfaces;
/**
 * 增加默认方法
 * @author 弓长小月
 *
 */
public interface CollectionDemo {
	/*
	 * 增加默认方法
	 */
	default void addObj(Object obj) {
		System.out.println("default add");
	}
	
	/*
	 * 删除默认方法
	 */
	default void delObj(Object obj) {
		System.out.println("default del");
	}
	/*
	 * 修改默认方法
	 */
	default void updateObj(Object obj) {
		System.out.println("default update");
	}
	/*
	 * 必须实现的方法
	 */
	String showMsg();
}
package interfaces;
/**
 * 增删改可以直接使用默认方法的实现
 * @author 弓长小月
 *
 */
public class DefaultMethods implements CollectionDemo{
	/*
	 * 这是必须实现的方法
	 * @see interfaces.CollectionDemo#showMsg()
	 */
	@Override
	public String showMsg() {
		// TODO Auto-generated method stub
		return null;
	}

}

我们假设有一个类不能删除元素,那么我们在实现该接口的时候可以有选择性的实现上述默认方法。

package interfaces;

public class NotDelMethod implements CollectionDemo{

	@Override
	public String showMsg() {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public void delObj(Object obj) {
		System.out.println("can not del");
	}

}

通过上述代码,大家可以很清楚的发现,如果在接口中定义默认方法,则子类不需要必须实现该默认实现,如果有特殊需求或者需要,则可以Override该实现。

需要注意

  • 如果一个类实现两个或两个以上接口,并且多个接口中包含统一默认方法,此时,编译器将报错。这种情况,我们必须让子类Override该方法,否则无法编译通过。
  • 在所有的情况,类实现的优先级高于接口的默认实现,也就是先使用自己类中定义的方法或者是父类中的方法。
  • 如果是一个接口继承了另外一个接口,2个接口中也包含相同的默认方法,那么继承接口的版本具有更高的优先级。比如A扩展了B接口,那么优先使用A类里面的test方法。
  • 通过使用super,可以显式的引用被继承接口的默认实现,语法如下:InterfaceName.super.methodName()。

猜你喜欢

转载自blog.csdn.net/zxyy2627/article/details/82628623