首页 > 编程语言 > 详细

Java接口

时间:2016-04-08 21:52:53      阅读:396      评论:0      收藏:0      [点我收藏+]

本文是《Java核心技术 卷1》中第六章接口与内部类中关于接口的阅读总结。

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);
}

这样,就会根据学生的成绩对学生数组进行排序了。

2 定义接口并将接口作为类型使用

有时,为了描述一个通用的功能,需要自己定义一个接口。定义接口需要使用interface关键字,表明这是一个接口。同类一样,接口也可以继承,也使用extends关键字,不过,接口可以继承一个或多个接口,接口之间使用逗号分隔。

在JDK1.8中,接口新增了默认(default)方法和静态(static)方法,也就是说,在JDK1.8中,一个接口可以包含如下内容:抽象方法、默认方法、静态方法和常量。

定义一个接口和定义一个类类似,不同的是,除非方法是默认的(使用default)关键字,否则不能实现这个方法,这个方法要在实现这个接口的类中实现。比如,我们要自己实现一个Office工具组件,里面有好多组件比如Word、Excel和PPT等,这些组件都有一些共同的方法,这里为了简单比如启动方法(start),就可以定义一个接口Officeable,然后让这些组件实现这个接口,代码如下:

package interfaceTest;

public interface Officeable {
	void start();
}

这就定义好了一个接口。在编写Word时,只需要定义start方法就好。下面是Word和Excel的代码,简便起见只有一个方法:

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...");
	}
}

接口不是类,不能使用new关键字实例化一个接口,不过,可以声明一个接口变量,然后使用这个变量引用一个实现了这个接口的类对象:

Officeable oa=new Word();

在上面的例子中,虽然不能创建一个Officeable接口对象,却可以引用一个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),也可以省略这个标签。

3 扩展接口

考虑如下一个接口明叫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);
}

这个时候问题出现了。由于一个类实现一个接口,就要实现接口中所有的方法。当在DoIt接口中添加一个方法后,所有实现了这个接口的类都要进行修改,因为接口中多了一个方法,这些类也要实现这个方法。这会是个费时费力的工作。为了避免这种情况发生,接口的实现者可以考虑实现一个新的接口,来继承老的接口。比如这样:

public interface DoItPlus extends DoIt {
	boolean didItWork(int i,double x,String s);
}

这时,实现了DoIt接口的所有类都可以选择继续实现老接口DoIt而不需要改动也可以实现新的接口DoItPlus。

不过到了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
	}
}

需要注意,使用default关键字一定要在接口中实现这个方法。这时,所有实现这个接口的类都不需要做任何改动就获得了更多的功能,甚至不需要重新编译,这是JDK1.8的新特性。

4 默认方法

4.1 默认方法

在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();
}

下面的类SimpleTimeClient实现了这个接口:

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);
}

在接口中又声明了一个新的方法。不过,这时实现TimeClient接口的SimpleTimeClient类也需要修改并实现getZonedDateTime方法。这时,与其将这个方法在接口中声明为抽象方法,不如声明成默认方法:

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方法了。

4.2 继承一个具有默认方法的接口

当需要继承一个具有默认方法的接口时,子接口对默认方法的处理可以有一下三种方式:

  • 不做任何处理。这时子接口将继承父接口的默认方法;
  • 重新声明这个方法。这样这个方法就成了抽象方法,实现这个子接口的类必须实现这个方法;
  • 重新定义这个方法。这样就覆盖了父接口的方法;

 如果像这样继承接口:

public interface AnotherTimeClient extends TimeClient { }

那么实现AnotherTimeClient接口的类也具有TimeClient.getZonedDateTime方法。

如果这样继承接口:

public interface AbstractZoneTimeClient extends TimeClient {
    public ZonedDateTime getZonedDateTime(String zoneString);
}

那么所有实现AbstractZoneTimeClient接口的类都要实现这个方法。

如果像这样继承接口:

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());
        }
    }
}

那么实现HandleInvalidTimeZoneClient接口的类都将拥有这个接口中定义的方法而不是父接口中的方法。

4.3 静态方法

除了默认方法,接口中还可以定义静态方法。这可以让你更加容易的主治库中的辅助方法,可以将静态函数放在特定接口中,而不是放在另一个单独的类中。就像上面接口中定义的静态方法一样。

就像在类中定义静态方法一样,在接口中定义静态方法也使用static关键词。在接口中定义静态方法也可以省略public关键词,会自动设置为public。

5 接口与抽象类

接口与抽象类有很多相似的地方,那么为什么要引入接口呢?

这时因为java中一个类只能继承一个父类,而不能继承多个类。不过接口的存在就满足了java的多继承需求,因为一个类可以继承一个或多个接口。这样,接口不但提供了多继承的大多数好处,同时还避免了多继承的复杂性和低效性。

在JDK1.8中,接口中也可以定义方法了,也就是接口把手伸向了抽象类的地盘,变得更像抽象类了。那么两者间还有什么不同么?

  • 抽象类不可以多重继承,但是接口可以;
  • 抽象类和接口的设计理念不同。抽象类表示的是“is-a”关系,而接口表示的是“like-a”关系;
  • 接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值;抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。

6 总结

  1. 接口的定义中可以包含方法的声明、默认方法、静态方法和常量。其中只有默认方法和静态方法可以在接口中定义方法;
  2. 实现一个接口的类必须实现接口中声明的所有方法;
  3. 接口名也可以当做类型名使用。

Java接口

原文:http://blog.csdn.net/u012877472/article/details/51096936

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!