Effective java笔记3--类和接口2

三、接口优于抽象类

java提供两种机制,可以用来定义一个允许多个实现的类型:接口和抽象类。由于java只允许单继承,所以,抽象类作为类型定义受到了极大的限制。

已有的类可以很容易被更新,以实现新的接口。你所需要做的是:增加要求的方法,如果这些方法原先还不存在的话;然后在类的声明上增加一个implements子句。

接口是定义mixin(混合类型)的理想选择。一个mixin是指这样的类型:一个类除了实现它的”基本类型(primary type)"之外,还可以实现这个mixin类型,以表明它提供了某些可供选择的行为。

例如,Comparable是一个mixin接口,它允许一个类表明它的实例可以与其他的可相互比较的对象进行排序操作,这样的接口之所以被称为mixin,是因为它允许可选的功能可被混合到一个类型的基本功能中。抽象类不能被用于定义mixin,同理因为单继承原因。

接口使得我们可以构造出非层次结构的类型框架。类型层次对于组织某些事物是非常合适的,但是其他有些事物并不能被整齐地组织成一个严格的层次结构。

例如,singer(歌唱家),另一个接口代表一个songwriter(作曲家):

public interface Singer{
     AudioClip Sing(Song s);
}

public interface Songwriter{
     Song compose(boolean hit);
}

接口使得安全性地增强一个类的功能成为可能,做法通过介绍的包装类(WrapperClass)模式。如果你用抽象类来定义类型,那么这样就使得程序员除了使用继承的手段来增加功能之外,没有别的选择途径。这样得到的类与包装类相比,功能更差,也更加脆弱。

可以把接口和抽象类的优点结合起来,对于你期望导出的每一个重要接口,都提供一个抽象的骨架实现(skeletal implementation)类。在这里接口的作用任然是定义类型,但是骨架实现类负责所有与接口实现相关的工作。骨架实现被称为AbstractInterface,这里的interface是所实现的接口的名字。

例如,Collections Framework为每个重要集合接口都提供了一个骨架实现,包括AbstractCollection为Collection接口、AbstractSet(为set接口)、AbstractList(为list接口)和AbstractMap(为Map接口).

   如果设计合理的话,利用骨架实现,程序员可以很容易的提供他们自己的接口实现。例如,下面是一个静态工厂方法,它包含一个完整的、功能全面的List实现:

//List adapter for int array
static List intArrayAsList(final int[] a){
     if(a==null)
          throw new NullPointerException();
     
     return new AbstractList(){
          public Object get(int i){
               return new Integer(a[i]);            
          }
          
          public int size(){
               return a.length;
          }
          
          public Object set(int i,Object o){
               int oldVal = a[i];
               a[i] = ((Integer)o).intValue();
               return new Integer(oldVal);
          }
     }
}

这个例子是一个Adapter,它使得一个int数组可以被看做一个Integer实例列表。由于int值和Integer实例之间来回转换开销,它的性能不会非常好。注意,这个例子只提供了一个静态工厂,并且这个类是一个可被访问的匿名类(anonymous class),它被隐藏在静态工厂的内部。

骨架实现的优美之处在于,它们为抽象类提供了实现上的帮助,但又没有强加“抽象类被用作类型定义时候”所特有的严格限制。对于一个接口的大多数实现来讲,扩展骨架实现是一个很显然的选择,但也确实只是一个选择而已。如果一个预先已经定义好的类无法扩展骨架实现类,那么,这个类总是可以手工实现这个接口。尽管如此,骨架实现类仍然能够有助于接口的实现。实现了这个接口的类可以把对于接口的方法的调用,转发到一个内部私有的实例上,而这个内部私有类扩展了骨架实现类。这项技术被称为模拟多重继承(simulated multiple inheritance),这项技术具有多重继承的绝大多数优点,并且避免了相应的缺陷。

嵌套类--优先考虑静态成员类

嵌套类(nested class)是指被定义在另一个类的内部的类,嵌套类存在的目的应该只是为它的外围类提供服务。如果一个嵌套类将来可能会用于其他的某个环境中,那么它应该是顶层类(top-level class).

嵌套类有四种:

  • 静态成员类(static member class);
  • 非静态成员类(nonstatic member class);
  • 匿名类(anonymous class);
  • 局部类(local class);

Member Types(成员类型)即作为外部类的一个成员存在,与外部类的属性、方法并列同级的类型。成员内部类中不能定义静态变量,但可以访问外部类的所有成员.

除了第一种,其他三种都被称为内部类(inner class),那什么时候使用哪种嵌套类呢?为什么这样做呢?

静态成员类是一种最简单的嵌套类。最好把它看做是一个普通的类,只是碰巧被声明在另一个类的内部而已。它可以访问外围类的所有成员,包括那些声明为私有的成员。静态成员类是外围类的一个静态成员,与其他的静态成员一样,也遵守同样的可访问性规则。如果它被声明为私有的,那么它只能在外围类的内部才可以被访问等等。

静态成员类的一个通常用法是作为公有的辅助类,仅与它的外部类一起使用时才有意义。

非静态内部类nested inner class:

内部类隐含有一个外部类的指针this,因此,它可以访问外部类的一切资源(当然包括private)
外部类访问内部类的成员,先要取得内部类的对象,并且取决于内部类成员的封装等级。
非静态内部类不能包含任何static成员.

匿名类的适用性有一些限制。

  1. 因为它们同时被声明和实例化,所以匿名类只能被用在代码中它将被实例化的那个点上。
  2. 因为匿名类没有名字,所以在它们被实例化之后,就不能够再对它们进行引用,如果不是这种情况就不能使用匿名类。
  3. 因为匿名类出现在表达式的中间,所以它们应该非常简短,可能是20行或者更少,太长的匿名类会影响程序的可读性。

匿名类常用用法:

A、匿名类的一个通常用法是创建一个函数对象(function object),比如Comparator实例。排序:

//Typical use of an anonymous class
Arrays.sort(args,new Comparator(){
     public int compare(Object o1,Object o2){
          return ((String)o1.length()-((String)o2).length();
     }  
});

 

B、匿名类的另一个常用的用法是创建一个过程对象(process object),比如Thread、Runnable或者TimerTask实例。

C、还有一个用法是在一个静态工厂方法的内部(参考IntArrayAsList方法)。

D、在复杂的类型安全枚举类型(它要求为每个实例提供单独的子类)中,用于公有的静态final域的初始化器中。

局部类是四种嵌套类中最少使用的类。在任何“可以声明局部变量”的地方,都可以声明局部类,并且局部类也遵守同样的作用域规则。即在方法中定义的内部类,与局部变量类似,在局部内部类前不加修饰符public或private,其范围为定义它的代码块。
注意:局部内部类中不可定义静态变量,可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的。

public class Outer {
    private int s = 100;
    private int out_i = 1;
    public void f(final int k){
      final int s = 200;
      int i = 1;
      final int j = 10;
      class Inner{ //定义在方法内部
          int s = 300;//可以定义与外部类同名的变量
          //static int m = 20;//不可以定义静态变量
          Inner(int k){
              inner_f(k);
          }
          int inner_i = 100;
          void inner_f(int k){
             System.out.println(out_i);//如果内部类没有与外部类同名的变量,在内部类中可以直接访问外部类的实例变量
             System.out.println(k);//*****可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的*****
             //System.out.println(i);
             System.out.println(s);//如果内部类中有与外部类同名的变量,直接用变量名访问的是内部类的变量
             System.out.println(this.s);//用"this.变量名" 访问的也是内部类变量
             System.out.println(Outer.this.s);//用外部"外部类类名.this.变量名" 访问的是外部类变量
         }
     }
     new Inner(k);
    }

    public static void main(String[] args) {
        //访问局部内部类必须先有外部类对象
        Outer out = new Outer();
        out.f(3);
    }
}

简而言之,共有四种不同的嵌套类,每一种都有自己的用途。

 

 

私有成员变量可以被子类继承吗

今天看spring源码,不经意间发现了一问题:在一个抽象类中定义了一个私有成员变量。

仔细想想:抽象类不能被实例化的,只能被子类继承。但是自从学java的继承只有,我们就知道,子类不能继承父类的私有成员变量或方法的。

问题:在该抽象方法中定义这个私有变量有什么用呢?或者说这个私有成员变量再什么地方用得到呢?

所以自己做了一个测试如下:

复制代码
public abstract class Fatherclass {
    private int privatenumber;
    
    public int getPrivatenumber() {
        return privatenumber;
    }
    public void setPrivatenumber(int privatenumber) {
        this.privatenumber = privatenumber;
    }
}


public class Childclass extends Fatherclass{

    public static void main(String[] args) {
        Childclass childclass=new Childclass();
        Childclass1 childclass1=new Childclass1();
        childclass.setPrivatenumber(125);
        System.out.println(childclass.getPrivatenumber());
        System.out.println(childclass1.getPrivatenumber());
    }
}



public class Childclass1 extends Fatherclass {
    
}
复制代码

运行结果如下:

125
0

 

通过测试,我们发现子类确实继承了父类的私有属性(也可以说是子类拥有一个属性,继承自父类,但是该属性的访问权限暂时不确定),但是我们无法通过子类直接访问该(继承自父类私有属性)的属性。我暂且该这种属性的访问权限起名为fatherprivate(意为继承自父类私有属性)

转自:https://www.cnblogs.com/yidaijiankuanzhongbuhui/p/8417051.html

 

posted on 2013-11-13 10:47  duanxz  阅读(453)  评论(0编辑  收藏  举报