内部类使用笔记

定义在DTO或VO类中,可以在外面访问的内部类

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 业务类
*/
@Data
public class BusinessOutDTO {

private Integer id;
private String name;
private List<BusinessDetailOutDTO> businessDetailList;

/**
* 业务明细类,属于业务的一部分
*/
@Data
public class BusinessDetailOutDTO {
private Integer id;
private String detailName;
}
}

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建+赋值
BusinessOutDTO businessOutDTO = new BusinessOutDTO();
businessOutDTO.setId(1);
businessOutDTO.setName("business-A");

BusinessOutDTO.BusinessDetailOutDTO businessDetailOutDTO = new BusinessOutDTO().new BusinessDetailOutDTO();// 需要先 new 外部类对象,才能创建内部类对象
businessDetailOutDTO.setId(1);
businessDetailOutDTO.setDetailName("businessDetail-a");

List<BusinessOutDTO.BusinessDetailOutDTO> businessDetailList = new ArrayList<>();
businessDetailList.add(businessDetailOutDTO);
businessOutDTO.setBusinessDetailList(businessDetailList);
System.out.println(businessOutDTO);
// 输出:BusinessOutDTO(id=1, name=business-A, businessDetailList=[BusinessOutDTO.BusinessDetailOutDTO(id=1, detailName=businessDetail-a)])


// 使用
List<BusinessOutDTO.BusinessDetailOutDTO> businessDetailList1 = businessOutDTO.getBusinessDetailList();
System.out.println(businessDetailList1);
// 输出:[BusinessOutDTO.BusinessDetailOutDTO(id=1, detailName=businessDetail-a)]

特点:

  1. 声明为 public,非 static,属于成员内部类
  2. 定义成内部类只是觉得它“属于”其外部类,其实把它定义成独立的非内部类也是可以的
  3. 因为外部类和内部类都是 DTO 类,无逻辑,所以只有外部类创建和使用到内部类,内部类不会访问外部类属性

在外部类中被用来做逻辑处理的内部类

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DeadlineUtil {

public static DeadlineBO add(int deadlineVal1, String deadlineUnit1, int deadlineVal2, String deadlineUnit2) {
DeadlineBO deadlineBO1 = new DeadlineUtil.DeadlineBO(deadlineVal1, deadlineUnit1); // 直接创建内部类对象,不需要先 new DeadlineUtil()
DeadlineBO deadlineBO2 = new DeadlineUtil.DeadlineBO(deadlineVal2, deadlineUnit2);

System.out.println("==============此处是一段期限相加逻辑==============");
DeadlineBO deadlineBOResult = new DeadlineUtil.DeadlineBO(0, "year");
return deadlineBOResult;
}

@Data
public static class DeadlineBO {
private int deadlineVal;
private String deadlineUnit;

public DeadlineBO(int deadlineVal, String deadlineUnit) {
this.deadlineVal = deadlineVal;
this.deadlineUnit = deadlineUnit;
}
}
}

内部类 DeadlineBO 被用来做逻辑处理,并作为方法的返回类型,注意其在外部类方法 add 方法的返回类型声明中,不用加外部类名前缀”DeadlineUtil.”。

使用示例:

1
DeadlineUtil.DeadlineBO addResult = DeadlineUtil.add(1, "year", 3, "month");

在外面使用时,还是会加外部类名前缀”DeadlineUtil.”。

另外,这个内部类被声明为“静态内部类”,静态内部类的特点是不需要 new 外部类对象就能使用,但也不能访问外部类的非静态成员。对于工具类而言,因为不希望创建工具类的对象,所以用静态内部类更合适。

如果没有声明为静态内部类(即没有static的内部类),那么上面的 add 方法会是这样:

1
2
3
4
5
6
7
8
public static DeadlineBO add(int deadlineVal1, String deadlineUnit1, int deadlineVal2, String deadlineUnit2) {
DeadlineBO deadlineBO1 = new DeadlineUtil().new DeadlineBO(deadlineVal1, deadlineUnit1); // 需要先 new DeadlineUtil() 才能创建内部类对象
DeadlineBO deadlineBO2 = new DeadlineUtil().new DeadlineBO(deadlineVal2, deadlineUnit2);

System.out.println("==============此处是一段期限相加逻辑==============");
DeadlineBO deadlineBOResult = new DeadlineUtil().new DeadlineBO(0, "year");
return deadlineBOResult;
}

特点:

  1. 声明为 public static,属于静态内部类
  2. 外部类会使用内部类作逻辑处理,所以外部类内部会创建和使用内部类对象,但内部类不会访问外部类属性

匿名内部类

创建匿名内部类之前,这个匿名内部类必须是继承自一个接口或抽象类。

常见的例子如创建线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainTest {
public static void main(String[] args) {
Runnable runnable = newRunnable();
new Thread(runnable).start();
}

public static Runnable newRunnable() {
return new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()); // Thread-0
}
};
}
}

Runnable 就是一个接口,Debug看newRunnable()方法返回的类型,会看到类似于”MainTest$1@555”字样,而上一个例子 DeadlineUtil.add() 方法返回的会是”DeadlineUtil$DeadlineBO”字样,区别就是”$”符号后面有没有具体的类名。而这个叫”MainTest$1@555”的对象就是外部类 MainTest 创建的“实现 Runnable 接口的匿名内部类”的对象。

如果不用这个匿名内部类,那么常规方法是:1)声明一个类实现 Runnable 接口及其 run 方法;2)在 MainTest 中创建1中声明的类的对象,使用这个对象

静态、非静态方法返回的匿名内部类

匿名内部类 作为 return 参数时,它所在的方法是 static 和非 static 的性质是不同的。

例如以下代码中两个方法都返回 Runnable 接口的匿名内部类对象,但一个是 static 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class RunnableLoader {

public Runnable noStaticRunnable() {
return new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
}

public static Runnable staticRunnable() {
return new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
}
}

分别调用这两个方法返回不同的 Runnable 对象,Debug看时,发现非 static 方法返回的对象中有一个属性this$0指向其外部类对象RunnableLoader,而 static 方法返回的就没有这个对象。原因就是外部类的 static 方法不会创建外部类对象,也不能访问外部类对象实例。

Debug代码与截图如下:

1
2
3
4
RunnableLoader runnableLoader = new RunnableLoader();
Runnable runnable = runnableLoader.noStaticRunnable();

Runnable staticRunnable = RunnableLoader.staticRunnable();

使用到匿名内部类以外的变量

这里说的“匿名内部类以外的变量”是说,这个变量既不是匿名内部类中声明的,也不是外部类声明的,例如下面这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class EnumerationLoader {

public Enumeration<String> getResource() {
Enumeration<String> e = this.get(); // 变量e是在这个方法体内声明的,既不是匿名内部类中声明的,也不是外部类声明的

return new Enumeration<String>() {
@Override
public boolean hasMoreElements() {
return e.hasMoreElements(); // 但内部类中用到了这个变量
}

@Override
public String nextElement() {
return e.nextElement();
}
};
}
}

Debug这个方法返回的匿名内部类对象,截图:

可以看到,这个变量Enumeration<String> e和外部类对象this$0一样,也被认为是匿名内部类的属性之一。