接口一章讲到抽象类和接口,单纯看书上的抽象类感觉很抽象。而且接口这一章的知识点,并没有像之前的讲的详细,而且碎且难,有几个问题是几个人在研讨会提出的,之前多态的有一个域的问题也是研讨会有人提出,那个相对容易理解一些。
1)抽象类
class TV{
public void show(){
System.out.println("TV");
}
}
class LeTV extends TV{
public void show(){
System.out.println("LeTV");
}
}
class MiTV extends TV{
public void show(){
System.out.println("MiTV");
}
}
class SanTV extends TV{
public void show(){
System.out.println("SanTV");
}
}
public class EveryTV {
public static void tvshow(LeTV tv){
tv.show();
}
public static void tvshow(MiTV tv){
tv.show();
}
public static void tvshow(SanTV tv){
tv.show();
}
public static void main(String[] args) {
tvshow(new LeTV());
tvshow(new MiTV());
tvshow(new SanTV());
}
}
java提供了一种抽象方法的机制,C++中叫纯虚函数。
包含抽象方法的类为抽象类,如果一个类有一个或多个的抽象方法,那么类一定要定义为抽象类。
抽象的方法声明可以没有方法体。如下:
abstract show();
抽象类不可new,确保抽象类的纯粹性,这就是抽象类,其实这也是抽象类存在的一个原因。
上述的TV类可以改写成抽象类:
abstract class TV{
abstract void show();
}
注意,子类同样需要继承方法,而且要有方法体。
其实电视本来就很抽象,TV本身不需要具体的show方法,具体的电视有具体的实现方法,new TV这个类也没什么用,TV就是一个抽象的概念。
看了一篇英文的回答——What is an abstract class, and when should it be used?
讲的是动物吃东西,动物本身也是一个抽象层次上的概念,但是动物有吃东西的方法,每种不同的动物有自己的吃法,吃肉,吃草,但是总得吃,动物这个概念性的东西,就可以声明为抽象类,如果是普通类,那么子类不重写也可以,那么默认就变成了和动物一样的吃法,但是动物本身并没有吃法,定义为抽象类,抽象方法在子类中需要有实现,这样扩展性也好,这应该就是设计者的初衷。
2)接口
public interface TV {}
不定义public的话,接口只具有包内的访问权限,而接口的方法不声明为public,也是public的,接口可以有域,隐式为static和final。
当然,我们完全可以将TV设计为一个接口。那么为什么有抽象类还要有接口呢?
接口有一个特定的地方,实现接口的类必须实现接口的所有方法,抽象类除外,抽象类可以选择性的实现。这个问题后面总结时解答。
3)完全解耦
耦合性是软件工程的一个概念,程序设计讲究高内聚低耦合,耦合性可以简单看成依赖性,就是粘合度过紧。
书先举了一个例子。
class TV{
public String name(){
return getClass().getSimpleName();//拿到类名
}
Object show(Object input){
return input;
}
}
class LeTV extends TV{
String show(Object input){
return (String)input+" in letv";
}
}
class MiTV extends TV{
String show(Object input){
return (String)input+" in mitv";
}
}
public class TVShow {
public static void play(TV tv , Object o){
System.out.println(tv.name());
System.out.println(tv.show(o));
}
public static void main(String[] args) {
TVShow ts = new TVShow();
String mylove = "my love tv show";
ts.play(new LeTV(), mylove);
ts.play(new MiTV(), mylove);
}
}
接下来有Computer,但是专门处理Game。
class Game{
private static int count;
private final int id = count++;
public String toString(){
return "Game"+id;
}
}
public class Computer{
public String name(){
return getClass().getSimpleName();//拿到类名
}
Game show(Game input){
return input;
}
}
class LeComputer extends Computer{
Game show(Game input){
return input;
}
}
class MiComputer extends Computer{
Game show(Game input){
return input;
}
}
所以,将TV设计为一个接口:
public interface TV {
String name();
Object show(Object input);
}
public class TVShow {
public static void play(TV tv , Object o){
System.out.println(tv.name());
System.out.println(tv.show(o));
}
}
public abstract class AbstractTV implements TV{
public String name() {
return getClass().getSimpleName();
}
public abstract String show(Object input);
public static void main(String[] args) {
TVShow ts = new TVShow();
String mylove = "my love tv show";
ts.play(new LeTV(), mylove);
ts.play(new MiTV(), mylove);
}
}
class LeTV extends AbstractTV{
public String show(Object input){
return (String)input+" in letv";
}
}
class MiTV extends AbstractTV{
public String show(Object input){
return (String)input+" in mitv";
}
}
class Game{
private static int count;
private final int id = count++;
public String toString(){
return "Game"+id;
}
}
class ComputerAdapter implements TV{
private Computer c ;
ComputerAdapter(Computer c){
this.c = c;
}
public String name() {
return c.name();
}
public Game show(Object input) {
return c.show((Game)input);
}
}
public class ComputerShow{
public String name(){
return getClass().getSimpleName();//拿到类名
}
Game show(Game input){
return input;
}
public static void main(String[] args) {
Game g = new Game();
TVShow.play(new ComputerAdapter(new LeComputer()),g);
TVShow.play(new ComputerAdapter(new MiComputer()),g);
}
}
class LeComputer extends Computer{
Game show(Game input){
return input;
}
}
class MiComputer extends Computer{
Game show(Game input){
return input;
}
}
class Computer{
public String name(){
return getClass().getSimpleName();//拿到类名
}
Game show(Game input){
return input;
}
}
发现没有,原来不能给Computer使用的TVShow的play方法现在可以用了,原因是ComputerAdapter实现了TV接口。
其实这种模式又是另外一种设计模式——适配器模式。ComputerAdapter接收不同的Computer同时实现TV接口,以便后面用于TVShow方法,不然单纯的Computer对象无法作为方法参数,适配器能将你所拥有的接口去产生所需要的接口,即这个例子的接口传入,其实很类似策略模式。ComputerAdapter本身就是代理,你只需要传入Computer对象,而看不到里面的方法实现。
还有一个有趣的地方,虽说是实现接口的所有方法,但是ComputerAdapter和AbstractTV实现接口的show方法时却与不同的类型,原因在Object上,如果TV接口的show方法不是Object类型而是某一具体的对象的话,那么实现方法的时候方法类型就要一致。
这种接口的实现用处就是降低耦合性,TVShow不仅能传入继承AbstractTV的对象,对于其他实现TV接口的对象也是可以使用的。刚开始看可能有点看懂,我看书也看了好久,一大堆东西,但总算搞明白了。
4)多重继承
由于只能继承一个类,但是可以实现多个接口,便拥有多个接口的功能。
public class A extends B implements C,D,E{}
5)接口的域
放在接口的域都是自动为static和final,同时也是public的。其实特性反过来证明也是可以的。练习题也有。
public interface T {
int A=1;
}
public class Test implements T{
public static void main(String[] args) {
System.out.println(Test.A);
//System.out.println(Test.A++);
}
}
静态域才可以通过类名直接访问,final的域不能改变,在java5开始,就有了枚举类型了。
最后,就像书上说的“确定接口是理想选择,因而应该总是选择接口而不是具体的类。”这其实是引诱。
通篇下来,会发现接口真的很抽象,抽象要应需求而用,而不是为了用了用。
这和设计模式是一样的道理。