本文是《Java核心技术 卷1》中第六章接口与内部类中关于接口的阅读总结。
接口(interface)技术,描述了类具有什么功能,但不给出具体的实现(JDK1.8中新增了default方法)。一个类可以实现(implements)一个或多个接口,并在需要接口的地方,随时使用实现了相应接口的对象。
接口不是类,而是对类的一组需求描述,这些实现这个接口的类要遵从接口描述的同一格式进行定义。
比如,Arrays类有一个sort方法,这个方法承诺可以对对象数组进行排序,但要求对象所属的类必须实现了Comparable接口:
public interface Comparable<T> { int compareTo(Object other); }这就是说,任何实现Comparable接口的类都需要包含compareTo方法,而且这个方法的定义必须和接口中声明的形式一样。
接口中所有的方法都自动的属于public,因此,在接口中声明方法时,可以不提供关键词public。
假如现在有一个学生类Student,要使用Arrays类中的sort方法对学生进行按成绩排序,那么就需要Student类事件Comparable接口。实现一个接口要使用implements关键字。如果要实现多个接口,那接口之间使用逗号分隔:
public class Student implements Comparable然后,就需要在类中定义接口中的所有方法。下面是Student中对compareTo方法的定义:
public int compareTo(Student o){ return Double.compare(this.score, o.score); }
这样,就会根据学生的成绩对学生数组进行排序了。
有时,为了描述一个通用的功能,需要自己定义一个接口。定义接口需要使用interface关键字,表明这是一个接口。同类一样,接口也可以继承,也使用extends关键字,不过,接口可以继承一个或多个接口,接口之间使用逗号分隔。
在JDK1.8中,接口新增了默认(default)方法和静态(static)方法,也就是说,在JDK1.8中,一个接口可以包含如下内容:抽象方法、默认方法、静态方法和常量。
定义一个接口和定义一个类类似,不同的是,除非方法是默认的(使用default)关键字,否则不能实现这个方法,这个方法要在实现这个接口的类中实现。比如,我们要自己实现一个Office工具组件,里面有好多组件比如Word、Excel和PPT等,这些组件都有一些共同的方法,这里为了简单比如启动方法(start),就可以定义一个接口Officeable,然后让这些组件实现这个接口,代码如下:
package interfaceTest; public interface Officeable { void start(); }
public class Word implements Officeable { @Override public void start() { System.out.println("Word is running..."); } } public class Excel implements Officeable { @Override public void start() { System.out.println("Excel is running..."); } }
Officeable oa=new Word();
public class interfaceTest { public static void main(String[] args){ Word w=new Word(); Officeable oa=w; oa.start(); } }
Word is running...
同时,接口中还可以包含一个常量,这个常量将自动设置为公有静态常量(public static final),也可以省略这个标签。
考虑如下一个接口明叫DoIt:
public interface DoIt { void doSomething(int i,double x); int doSomethingElse(String s); }这个接口有两个方法doSomething和doSomethingElse,这个接口已经被很多人使用了。不过,过了一段时间,开发者想在这里添加一个方法,那么接口可能会是这样:
public interface DoIt { void doSomething(int i,double x); int doSomethingElse(String s); boolean didItWork(int i,double x,String s); }
public interface DoItPlus extends DoIt { boolean didItWork(int i,double x,String s); }
不过到了JDK1.8中,接口的开发者有了新的选择,在接口中实现一个默认方法:
public interface DoIt { void doSomething(int i,double x); int doSomethingElse(String s); default boolean didItWork(int i,double x,String s){ //method body } }
在JDK1.8中,添加了一个特性,就是接口中也可以定义方法了。不过这个方法必须是默认的(default)或静态的(static)。默认方法可以增加接口的功能,同时还能保证对旧接口的兼容性。
考虑下面的接口TimeClient,可以设置并获取时间:
import java.time.*; public interface TimeClient { void setTime(int hour,int minute,int second); void setDate(int day,int month,int year); void setDateAndTime(int day,int month,int year, int hour,int minute,int second); LocalDateTime getLocalDateTime(); }
import java.time.*; import java.lang.*; import java.util.*; public class SimpleTimeClient implements TimeClient { private LocalDateTime dateAndTime; public SimpleTimeClient() { dateAndTime = LocalDateTime.now(); } public void setTime(int hour, int minute, int second) { LocalDate currentDate = LocalDate.from(dateAndTime); LocalTime timeToSet = LocalTime.of(hour, minute, second); dateAndTime = LocalDateTime.of(currentDate, timeToSet); } public void setDate(int day, int month, int year) { LocalDate dateToSet = LocalDate.of(day, month, year); LocalTime currentTime = LocalTime.from(dateAndTime); dateAndTime = LocalDateTime.of(dateToSet, currentTime); } public void setDateAndTime(int day, int month, int year, int hour, int minute, int second) { LocalDate dateToSet = LocalDate.of(day, month, year); LocalTime timeToSet = LocalTime.of(hour, minute, second); dateAndTime = LocalDateTime.of(dateToSet, timeToSet); } public LocalDateTime getLocalDateTime() { return dateAndTime; } public String toString() { return dateAndTime.toString(); } public static void main(String... args) { TimeClient myTimeClient = new SimpleTimeClient(); System.out.println(myTimeClient.toString()); } }过一段时间,希望在TimeClient接口中增加功能,比如获得时区的功能,这时,接口可能这样:
import java.time.*; public interface TimeClient { void setTime(int hour,int minute,int second); void setDate(int day,int month,int year); void setDateAndTime(int day,int month,int year, int hour,int minute,int second); LocalDateTime getLocalDateTime(); ZonedDateTime getZonedDateTime(String zoneString); }
import java.time.*; public interface TimeClient { void setTime(int hour, int minute, int second); void setDate(int day, int month, int year); void setDateAndTime(int day, int month, int year, int hour, int minute, int second); LocalDateTime getLocalDateTime(); static ZoneId getZoneId (String zoneString) { try { return ZoneId.of(zoneString); } catch (DateTimeException e) { System.err.println("Invalid time zone: " + zoneString + "; using default time zone instead."); return ZoneId.systemDefault(); } } default ZonedDateTime getZonedDateTime(String zoneString) { return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString)); } }在声明方法前使用default关键字来指定一个方法是默认方法。由于接口中所有的方法默认都认为是public,所以可以省略public关键字。
这样,在SimpleTimeClient类和其它所有实现了TimeClient接口的类中都不需要做任何修改就有已经定义好的getZonedDateTime方法了。
当需要继承一个具有默认方法的接口时,子接口对默认方法的处理可以有一下三种方式:
如果像这样继承接口:
public interface AnotherTimeClient extends TimeClient { }
如果这样继承接口:
public interface AbstractZoneTimeClient extends TimeClient { public ZonedDateTime getZonedDateTime(String zoneString); }
如果像这样继承接口:
public interface HandleInvalidTimeZoneClient extends TimeClient { default public ZonedDateTime getZonedDateTime(String zoneString) { try { return ZonedDateTime.of(getLocalDateTime(),ZoneId.of(zoneString)); } catch (DateTimeException e) { System.err.println("Invalid zone ID: " + zoneString + "; using the default time zone instead."); return ZonedDateTime.of(getLocalDateTime(),ZoneId.systemDefault()); } } }
除了默认方法,接口中还可以定义静态方法。这可以让你更加容易的主治库中的辅助方法,可以将静态函数放在特定接口中,而不是放在另一个单独的类中。就像上面接口中定义的静态方法一样。
就像在类中定义静态方法一样,在接口中定义静态方法也使用static关键词。在接口中定义静态方法也可以省略public关键词,会自动设置为public。
接口与抽象类有很多相似的地方,那么为什么要引入接口呢?
这时因为java中一个类只能继承一个父类,而不能继承多个类。不过接口的存在就满足了java的多继承需求,因为一个类可以继承一个或多个接口。这样,接口不但提供了多继承的大多数好处,同时还避免了多继承的复杂性和低效性。
在JDK1.8中,接口中也可以定义方法了,也就是接口把手伸向了抽象类的地盘,变得更像抽象类了。那么两者间还有什么不同么?
原文:http://blog.csdn.net/u012877472/article/details/51096936