前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java--多态性之内部类和匿名类

Java--多态性之内部类和匿名类

作者头像
SuperHeroes
发布2018-05-22 16:12:43
6320
发布2018-05-22 16:12:43
举报
文章被收录于专栏:云霄雨霁

上一篇:多态性之抽象类和接口

为什么使用内部类:

每个内部类都可以独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了一个(接口的)实现,对内部类都没有影响。

如果没有内部类提供的、可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得更加完整。接口解决了部分问题,而内部类有效地实现了”多重继承“。也就是说,内部类允许继承多个非接口类型(类或抽象类)。

定义在一个类内部的类被称为内部类。内部类拥有对封装类所有元素的访问权限,因为内部类的对象默认持有创建它的那个封装类的一个对象的句柄。

若想在除外部类非 static 方法内部之外的任何地方生成内部类的一个对象,必须将那个对象的类型设为“外部类名.内部类名”,而且创建内部类实例必须先有一个外部类实例。

代码语言:javascript
复制
public class School{
    class Student{    //内部类
        int name;
    }
    public Student make(){
        return new Student();
    }
    public static void main(String[] args){
        School sh = new School();
        School.Student st = sh.make();    //生成内部类对象
    }
}

内部类的特点:

  • 数据隐藏
  • 能够访问外围对象的所有成员。

.this和 .new:

如果需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟原点+this。

如果想创建某个内部类对象,必须在new表达式中提供对其他外部类的引用,这需要.new语法,例如:

代码语言:javascript
复制
public static void main(String[] args){
    Outer outer = new Outer();
    Outer.Inner inner = outer.new Inner();
}

内部类和上溯造型:

当我们准备上溯造型到一个基础类(特别是到一个接口)的时候,内部类就开始发挥其关键作用。这是由于内部类随后可完全进入不可见或不可用状态——对任何人都将如此。所以我们可以非常方便地隐藏实施细节。我们得到的全部回报就是一个基础类或者接口的句柄,而且甚至有可能不知道准确的类型。

代码语言:javascript
复制
interface Destination {    //接口
   String readLabel();
}
public class Parcel3 {
   protected class PDestination implements Destination {    //内部类
       public String readLabel() { System.out.println("readLabel"); }
   }
   public Destination dest() {
       return new PDestination();
   }
}
class Test {
    public static void main(String[] args) {
        Parcel3 p = new Parcel3();
        Destination d = p.dest();    //上溯造型
    }
} 

定义在方法和作用域中的内部类:

上面的情况是使用内部类的经典情况,但内部类还有其他的用法:

  1. 在一个方法内定义的类
  2. 在方法的一个作用域内定义的类
  3. 一个匿名类,用于实现一个接口
  4. 一个匿名类,用于扩展拥有非默认构建器的一个类
  5. 一个匿名类,用于执行字段初始化
  6. 一个匿名类,通过实例初始化进行构建(匿名内部类不可拥有构建器)

采用方法或作用域中的内部类有两个原因:

  • 我们准备实现某种形式的接口,使自己能创建和返回一个句柄
  • 要解决一个复杂的问题,并希望创建一个类,用来辅助自己的程序方案。同时不愿意把它公开

下面这个例子是在方法的作用域中定义内部类

代码语言:javascript
复制
interface Dec {
    String readLabel();
}
public class Par {
    public Dec dest() {    //含有内部类的方法
        class PDec implements Dec {    //内部类继承接口
            public String readLabel() { System.out.println("readLabel"); }
        }
        return new PDec();    //上溯造型
    }
    public static void main(String[] args) {
        Par p = new Par();
        Dec d = p.dest();
    }
} 

仔细分析上述代码,PDec类属于dest()的一部分而不是Par()的一部分,所以PDec不能在dest()的外部被访问。请注意在返回语句中发生的上溯造型——除了指向基础类Dec的一个句柄之外,没有任何东西超出dest()的边界之外。当然, 不能由于类PDec的名字置于 dest()内部,就认为在 dest()返回之后 PDestination 不是一个有效的对象。

任意定义域中的内部类:

代码语言:javascript
复制
if(b) {
    class Move {
        //...
    }
    Move m = new Move();
    //...
}

在定义的范围外,这个内部类是不可见的,除此之外它和普通类没什么区别。

匿名内部类:

代码语言:javascript
复制
public class Parcel6 {
    public Contents cont() {
        return new Contents() {    //匿名类
            private int i = 11;
            public int value() { return i; }
        }; //必须用分号
    }
    //main()方法
}

上面代码包含一个匿名类--它没有名字。这种奇怪的语法要表达的意思是:“创建从 Contents 衍生出来的匿名类的一个对象”。由 new 表达式返回的 句柄会自动上溯造型成一个Contents 句柄。上述匿名类代码等同于:

代码语言:javascript
复制
class MyContents extends Contents {
    private int i = 11;
    public int value() { return i; }
}
return new MyContents();

由于没有名字,所以匿名类没有构造器,所以我们也不能直接生成一个匿名类的对象,但我们可以通过包含匿名类的方法来实现:

代码语言:javascript
复制
public class Par {
    public Destination dest(final String dest) {
        return new Destination() {
            private String label = dest;
            public String readLabel() { return label; }
        };
    }
    public static void main(String[] args) {
        Par p = new Par();
        Destination d = p.dest("Tanzania");
    }
}

分析上述代码,我们可以通过对dest()方法的调用来生成匿名类对象,而且该方法可以认为是匿名类的构建方法,当然它的功能是有限的:因为无法重载,所以“构建器”只能有一个。

从内部类(非static)继承:

由于内部类构建器必须同封装类对象的一个句柄联系到一起(见上面生成内部类对象的代码),所以从一个内部类继承的时候,情况会稍微变得有些复杂。这儿的问题是封装类的“秘密”句柄必须获得初始化,而且在衍生类中不再有一个默认的对象可以连接。解决这个问题的办法是采用一种特殊的语法,明确建立这种关联:

代码语言:javascript
复制
class WithInner {
    class Inner {}
}
public class InheritInner extends WithInner.Inner {    //继承内部类
    //! InheritInner() {} // 不会通过编译,因为继承的内部类的封装类没有显式初始化
    InheritInner(WithInner wi) {
        wi.super();    //保证封装类得到初始化
    }
    public static void main(String[] args) {
        WithInner wi = new WithInner();
        InheritInner ii = new InheritInner(wi);
 }
} 

下一篇:多态性之嵌套类

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档