可以将一个类的定义放在另一个类的内部,这就是内部类,这篇文章我们将对内部类进行一个详尽的介绍。
在最初内部类看是一种代码隐藏机制,但是远不止如此,他了解外围并能与之通信。
1.创建内部类
创建内部类如你想想的一样,把类的定义放在外围类的里面
public class Clien {
class Contents{
private int i = 11;
public int value(){
return i;
}
}
class Destination{
private String label;
public Destination(String whereto) {
label = whereto;
}
String readLabel(){
return label;
}
}
public void ship(String dest)
{
Contents c = new Contents();
Destination d = new Destination(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Clien c = new Clien();
c.ship("java");
}
}
outout:java
可以看到,在ship()里使用内部类与普通类没什么区别,只是内部类的名字嵌套在Clien里面,不过这不是唯一区别。
更典型的是外部类有一个方法返回对内部类的应用,就像to()和contents()方法那样。
public class parcel2 {
class Contents{
private int i = 11;
public int value(){
return i;
}
}
class Destination{
private String label;
public Destination(String whereto) {
label = whereto;
}
String readLabel(){
return label;
}
}
public Destination to(String s)
{
return new Destination(s);
}
public Contents contents()
{
return new Contents();
}
public void ship(String dest)
{
Contents c = new Contents();
Destination d = new Destination(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
parcel2 p = new parcel2();
p.ship("java");
parcel2 p2 = new parcel2();
//定义内部类的引用
parcel2.Contents c = p.contents();
parcel2.Destination destination = p2.to("java");
}
}
如果想从外部类非静态方法之外的任意位置创建某个内部类的对象,那么就要像main()方法中那样,具体指明这个对象的类型,OutClassName.InnerClassName。但是在ship()中不用。
2.连接到外部类
内部类拥有外部类所有元素的访问权,不需要任何条件。
interface Selector{
Object i = null;
boolean end();
Object current();
void next();
}
public class Sequence {
private Object[] items;
private int next = 0;
public Sequence(int size){ items = new Object[size];}
public void add(Object x){
if(next <items.length){
items[next++] = x;
}
}
private class SequenceSelector implements Selector{
private int i = 0;
@Override
public boolean end() {return i == items.length;}
@Override
public Object current() {return items[i];}
@Override
public void next() { if(i<items.length)i++;}
}
public Selector selector(){
return new SequenceSelector();
}
public static void main(String[] args) {
Sequence sequence = new Sequence(10);
for(int i = 0;i < 10;i++){
sequence.add(Integer.toString(i));
}
Selector selector = sequence.selector();
while(!selector.end()){
System.out.print(selector.current()+" ");
selector.next();
}
}
}
可以看到SequenceSelector这个内部类调用了外部类的方法和字段。内部类自动拥有对外部类所有成员的访问权限。
3.使用.this和.new
如果你需要生成对外围类对象的引用,可以使用外部类对象的名字加圆点和this。在编译时期受到检查,没有运行时开销。
public class DotThis {
public void f(){System.out.println("DotThis.f()");}
public class Inner{
public DotThis Outer(){
return DotThis.this;
}
}
public Inner inner(){return new Inner();}
public static void main(String[] args)
{
DotThis dt = new DotThis();
DotThis.Inner dti = dt.inner();
dti.Outer().f();
}
}
有时候你可能要告知某些其他对象,去创建某个内部类的对象。要实现此目的你必须在new表达式中提供对其他外部类对象的引用这时候需要使用.new语法
如代码所示。这就不需要上面的 inner()方法了直接用dt.new Inner();可以创建新的内部类。想要创建内部类对象你不能按照你想想的方式去引用外部类的名字DotNew,而是必须使用外部类对象来创建该内部类对象。所以你不能声明dn.new DotThis.Inner();。public static void main(String[] args) { DotThis dt = new DotThis(); DotThis.Inner dtn = dt.new Inner(); dtn.Outer().f(); }
在创建外部类对象之前是不可能创建内部类对象的,因为内部类对象是暗暗的连接到他的外部类对象上的。但是如果你创建的是嵌套类(静态内部类),那么就不需要对外部类对象的引用。
public class parcel3 {
class Contents{
private int i = 11;
public int value(){return i;}
}
static class Destination{
private String label;
public Destination(String whereto) {label = whereto;}
String readLabel(){return label;}
}
public static void main(String[] args) {
parcel3 p = new parcel3();
parcel3.Contents c = p.new Contents();
//parcel3.Destination d = p.new Destination("java");
parcel3.Destination d2 = new Destination("java");
}
}
如
Destination是静态内部类在创建的时候直接是parcel3.Destination d2 = new Destination("java");没有外部类的引用。
4.内部类与向上转型
将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了用武之地。这是因为内部类接口的实现,能完全不可见,并且不可用,所得到的只是指向基类或者接口的使用所以可以隐藏细节。
public interface Destination {
String ReadLable();
}
public interface Contents {
int value();
}
public class parcel4 {
private class pContents implements Contents{
private int i = 11;
@Override
public int value() {return i;}
}
protected class pDestnation implements Destination{
private String label;
private pDestnation(String whereto){label = whereto;}
public String ReadLable(){return label;}
}
public Destination destnation(String s){
return new pDestnation(s);
}
public Contents constents(){
return new pContents();
}
public static void main(String[] args) {
parcel4 p = new parcel4();
Contents c = p.constents();
Destination d = p.destnation("java");
//非法
//parcel4.Contents p2 = p.new pContents();
}
}
内部类pContents是private所以除了parcel4能访问它没人能访问,pDestination时protected的所以访问权限受到约束,所以完全隐藏了实现的细节,而且扩展接口也没有了价值,编译器可以生成更高效的代码。
5.在方法和作用域内的内部类
前面都是内部类的典型应用。在方法和作用域里定义内部的原因是
1)如前所示,实现了某个接口,于是可以创建并返回对其引用。
2)你要解决一个复杂的问题,想创建一个类来解决方案,但是这个类不是公共的。
下面要修改先前的代码,以用来实现
1)一个定义在方法中的类。
2)一个定义在作用域中的类,此作用域在方法的内部。
3)一个实现了接口的匿名类。
4)一个匿名类,它扩展了有非默认构造器的类。
5)一个匿名类,它执行字段初始化。
6)一个匿名类,它通过实例初始化实现构造(匿名类不可能有构造器)。
例1.局部内部类:在方法的作用域内。
public class parcel5 {
public Destination destination(String s){
class pDestination implements Destination{
private String label;
private pDestination(String whereto){label = whereto;}
@Override
public String ReadLable() {return label;}
}
return new pDestination(s);
}
public static void main(String[] args) {
parcel5 p = new parcel5();
//Destination pd = new pDestination();非法
Destination d = p.destination("java");
System.out.println(d.ReadLable());
}
}
在destination之外不能访问pDestination,当destination()方法执行完不代表pDestination不能用了。
例2:在任意作用域内嵌入一个内部类。
public class parcel6 {
private void internalTracking(boolean b){
if(b){
class TrackingSlip{
private String id;
public TrackingSlip(String s) {
this.id = s;
}
String getSlip(){return id;}
}
TrackingSlip ts = new TrackingSlip("slip");
String s = ts.getSlip();
}
//这里不能用
//TrackingSlip ts = new TrackingSlip("x");
}
public static void main(String[] args) {
parcel6 p = new parcel6();
p.internalTracking(true);
}
}
可以看到在定义域之外是不可用的。
6.匿名内部类
public class parcel7 {
public Contents constents(){
return new Contents() {
private int i = 11;
@Override
public int value() {
// TODO Auto-generated method stub
return i;
}
};
}
public static void main(String[] args) {
// TODO Auto-generated method stub
parcel7 p = new parcel7();
Contents c = p.constents();
}
}
contents()方法将返回值的生成与表示这个返回值的类的定义结合在了一起,这么使用的都是抽象类或者接口,也可以是类,但是会当作接口处理。
我的理解是内部类主要是为了实现接口所以会有大括号{},是为了实现接口里面的方法。
它的简化形式是
它的简化形式是
public class parcel7b {
class MyContents implements Contents{
private int i = 11;
@Override
public int value() {
// TODO Auto-generated method stub
return i;
}
}
public Contents contents(){return new MyContents();}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
parcel7 p = new parcel7();
Contents c = p.constents();
}
}
在这个匿名内部类中使用了默认的构造器来生成Contents。下面代码展示如果你的基类需要一个有参数的构造器,怎么办
public class Wrapping {
private int i;
public Wrapping(int x) { i = x; }
public int value() { return i; }
}
public class parcel8 {
public Wrapping wrapping(int x){
return new Wrapping(x){
public int value(){
return super.value()*47;
}
};
}
public static void main(String[] args) {
// TODO Auto-generated method stub
parcel8 p = new parcel8();
Wrapping w = p.wrapping(3);
System.out.println(w.value());
}
}
只需简单的传递合适得参数给基类构造器就可。这里将x传递给Wrapping(x),尽管Wrapping是一个普通类,但是还是被导出类当做公共“接口”来使用。
你还应该注意到Wrapping拥有一个要求传递一个参数的构造器。
在匿名类中定义字段时还能对其进行初始化操作。
public class parcel9 {
public Destination destination(final String dest){
return new Destination(){
private String label = dest;
public String ReadLable() {return label;}
};
}
public static void main(String[] args) {
// TODO Auto-generated method stub
parcel9 p = new parcel9();
Destination d = p.destination("java");
System.out.println(d.ReadLable());
}
}
如果定义一个匿名内部类,并希望使用外部定义的一个对象,那么编译器要求其参数应用必须是final的。否则报错。
内部类不可能有命名构造器,通过用实例初始化达到为内部类创建构造器的
效果
abstract class Base{
public Base(int i){
println("Base Constructor i = "+i);
}
public abstract void f();
}
public class AnonymousConstructor {
public static Base getBase(int i){
return new Base(i){
{println("Inside instance initializer");}
@Override
public void f() {
println("In anoymout f()");
}
};
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Base b = getBase(47);
b.f();
}
}
在这里i就不用final了,因为它不会被匿名内部类直接引用。
下面是才实例初始化的形式。
public class parcel10 {
public Destination destination(final String dest,final float price){
return new Destination(){
private int cost;
{
cost = Math.round(price);
if(cost>100)System.out.println("over budget");
}
private String label = dest;
public String ReadLable() { return label;}
};
}
public static void main(String[] args) {
// TODO Auto-generated method stub
parcel10 p = new parcel10();
Destination d = p.destination("java", 101.33F);
}
}
下面有具体的实例
1)
package inner_class;
import static net.lijun.util.Print.*;
interface Service{
void method1();
void method2();
}
interface ServiceFactory{
public Service getService();
}
class Implementation1 implements Service{
private Implementation1(){}
public void method1() {
// TODO Auto-generated method stub
println("Implementation1 method1");
}
public void method2() {
// TODO Auto-generated method stub
println("Implementation1 method2");
}
public static ServiceFactory factory(){
return new ServiceFactory() {
public Service getService() {
// TODO Auto-generated method stub
return new Implementation1();
}
};
}
}
class Implementation2 implements Service{
private Implementation2(){}
public void method1() {
// TODO Auto-generated method stub
println("Implementation2 method1");
}
public void method2() {
// TODO Auto-generated method stub
println("Implementation2 method2");
}
public static ServiceFactory factory(){
return new ServiceFactory() {
public Service getService() {
// TODO Auto-generated method stub
return new Implementation2();
}
};
}
}
public class Factories {
public static void serviceConsumer(ServiceFactory factory){
Service s = factory.getService();
s.method1();
s.method2();
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
serviceConsumer(Implementation1.factory());
serviceConsumer(Implementation2.factory());
}
}
2)
package inner_class;
import static net.lijun.util.Print.*;
interface Game{boolean move();}
interface GameFactory{Game getGame();}
class Checkers implements Game{
private Checkers(){}
private int moves = 0;
private final int MOVES = 3;
public boolean move() {
println("Checkers move "+moves);
return ++moves == MOVES;
}
public static GameFactory factor = new GameFactory() {
public Game getGame() {
// TODO Auto-generated method stub
return new Checkers();
}
};
}
class Chess implements Game{
private Chess(){}
private int moves = 0;
private final int MOVES = 4;
public boolean move() {
println("Checkers move "+moves);
return ++moves == MOVES;
}
public static GameFactory factor = new GameFactory() {
public Game getGame() {
// TODO Auto-generated method stub
return new Chess();
}
};
}
public class Games {
public static void PlayGame(GameFactory factory){
Game s = factory.getGame();
while(!s.move());
}
public static void main(String[] args) {
PlayGame(Checkers.factor);
PlayGame(Chess.factor);
}
}
/*output:
Checkers move 0
Checkers move 1
Checkers move 2
Checkers move 0
Checkers move 1
Checkers move 2
Checkers move 3*/
下次开始嵌套类
未完待续