ZB-014-java接口和抽象类02

Comparable 接口

1
2
3
4
5
6
7
8
9
public interface Comparable<T> {
/**
* Compares this object with the specified object for order. Returns a
* negative integer, zero, or a positive integer as this object is less
* than, equal to, or greater than the specified object.
...
*/
public int compareTo(T o);
}
  • natural ordering 自然顺序(从小到大)
1
2
3
a < b 返回 -1
a > b 返回 1
a = b 返回 0

实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package com.github.hcsp.polymorphism;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class Point implements Comparable<Point>{

private final int x;
private final int y;
// 代表笛卡尔坐标系中的一个点
public Point(int x, int y) {
this.x = x;
this.y = y;
}

public int getX() {
return x;
}

public int getY() {
return y;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

Point point = (Point) o;

if (x != point.x) {
return false;
}
return y == point.y;
}

@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}

@Override
public String toString() {
return String.format("(%d,%d)", x, y);
}

// 按照先x再y,从小到大的顺序排序
// 例如排序后的结果应该是 (-1, 1) (1, -1) (2, -1) (2, 0) (2, 1)
public static List<Point> sort(List<Point> points) {
Collections.sort(points);
return points;
}

public static void main(String[] args) throws IOException {
List<Point> points =
Arrays.asList(
new Point(2, 0),
new Point(-1, 1),
new Point(1, -1),
new Point(2, 1),
new Point(2, -1));
System.out.println(Point.sort(points));
}

@Override
public int compareTo(Point that) {
// 比较 this 和 that
if(this.x < that.x){
return -1;
}else if(this.x > that.x){
return 1;
}

// 运行到此说明 this.x = that.x
// 因此我要可以进行 y 的排序
if(this.y < that.y){
return -1;
}else if(this.y > that.y){
return 1;
}

return 0;
}
}
  • 一个坑的地方千万不要这样比较 return this.x - that.x;
    • 记得溢出吗?
    • 两个数相减,不一定是你想要的那个数,同理两数相加也是一个道理
  • Comparable 实际也是策略模式,由你来决定策略的各种条件
  • 参考练习
  • 我的pr

另一个坑,注意ComparableTreeSet中的坑

  • Set 它是一个 无重复元素的集合
  • 而你对TreeSet 排序时候也就是实现Comparable 接口的,而调用compareTo时候
    • 一旦 o1.id = o2.id 导致compareTo 返回 0 这样就会丢失元素
    • compareTo不要返回0,否则一定踩到坑
  • TreeSet排序丢失元素的bug
  • 我的pr
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    public class User implements Comparable<User> {
    /** 用户ID,数据库主键,全局唯一 */
    private final Integer id;

    /** 用户名 */
    private final String name;

    public User(Integer id, String name) {
    this.id = id;
    this.name = name;
    }

    public Integer getId() {
    return id;
    }

    public String getName() {
    return name;
    }

    @Override
    public boolean equals(Object o) {
    if (this == o) {
    return true;
    }
    if (o == null || getClass() != o.getClass()) {
    return false;
    }

    User person = (User) o;

    return Objects.equals(id, person.id);
    }

    @Override
    public int hashCode() {
    return id != null ? id.hashCode() : 0;
    }

    /** 老板说让我按照用户名排序 */
    @Override
    public int compareTo(User o) {
    if(name == o.name){
    return id.compareTo(o.id);
    }
    return name.compareTo(o.name);
    }

    public static void main(String[] args) {
    List<User> users =
    Arrays.asList(
    new User(100, "b"),
    new User(10, "z"),
    new User(1, "a"),
    new User(2000, "a"));
    TreeSet<User> treeSet = new TreeSet<>(users);
    // 为什么这里的输出是3?试着修复其中的bug
    System.out.println(treeSet.size());
    }
    }

实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.github.hcsp.polymorphism;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class User {
/** 用户ID,数据库主键,全局唯一 */
private final Integer id;

/** 用户名 */
private final String name;

public User(Integer id, String name) {
this.id = id;
this.name = name;
}

public Integer getId() {
return id;
}

public String getName() {
return name;
}

// 过滤ID为偶数的用户
public static List<User> filterUsersWithEvenId(List<User> users) {
List<User> results = new ArrayList<>();
for (User user : users) {
if (user.id % 2 == 0) {
results.add(user);
}
}
return results;
}

// 过滤姓张的用户
public static List<User> filterZhangUsers(List<User> users) {
List<User> results = new ArrayList<>();
for (User user : users) {
if (user.name.startsWith("张")) {
results.add(user);
}
}
return results;
}

// 过滤姓王的用户
public static List<User> filterWangUsers(List<User> users) {
List<User> results = new ArrayList<>();
for (User user : users) {
if (user.name.startsWith("王")) {
results.add(user);
}
}
return results;
}
// 你可以发现,在上面三个函数中包含大量的重复代码。
// 请尝试通过Predicate接口将上述代码抽取成一个公用的过滤器函数
// 并简化上面三个函数
public static List<User> filter(List<User> users, Predicate<User> predicate) {}
}

用策略模式 和 匿名内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
public class User {
/** 用户ID,数据库主键,全局唯一 */
private final Integer id;

/** 用户名 */
private final String name;

public User(Integer id, String name) {
this.id = id;
this.name = name;
}

public Integer getId() {
return id;
}

public String getName() {
return name;
}


// 你可以发现,在上面三个函数中包含大量的重复代码。
// 请尝试通过Predicate接口将上述代码抽取成一个公用的过滤器函数
// 并简化上面三个函数
public static List<User> filter(List<User> users, 判断条件是否成立 条件) {
List<User> results = new ArrayList<>();
for (User user: users) {
if(条件.这个用户是否满足条件(user)){
results.add(user);
}
}
return results;
}


private interface 判断条件是否成立 {
boolean 这个用户是否满足条件(User user);
}

private static class 用户ID是偶数的条件 implements 判断条件是否成立{

@Override
public boolean 这个用户是否满足条件(User user) {
return user.id % 2 == 0;
}
}

public static void main(String[] args) {
List<User> res = filterUsersWithEvenId(Arrays.asList(new User(1,"a"),new User(2,"b")));
System.out.println(res);

List<User> res2 = filterZhangUsers(Arrays.asList(new User(1,"张三"),new User(2,"李四")));
System.out.println(res2);
}

// 过滤ID为偶数的用户(接口实现类的实例)
public static List<User> filterUsersWithEvenId(List<User> users) {
List<User> results = new ArrayList<>();
return filter(users,new 用户ID是偶数的条件());
}

// 过滤姓张的用户(匿名内部类)
public static List<User> filterZhangUsers(List<User> users) {
return filter(users, new 判断条件是否成立() {
@Override
public boolean 这个用户是否满足条件(User user) {
return user.name.startsWith("张");
}
});
}

// 过滤姓王的用户
public static List<User> filterWangUsers(List<User> users) {
List<User> results = new ArrayList<>();
for (User user : users) {
if (user.name.startsWith("王")) {
results.add(user);
}
}
return results;
}
}

Predicate

  • 判定
  • 接口内部只有一个方法,返回值是boolean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.github.hcsp.polymorphism;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class User {
/** 用户ID,数据库主键,全局唯一 */
private final Integer id;

/** 用户名 */
private final String name;

public User(Integer id, String name) {
this.id = id;
this.name = name;
}

public Integer getId() {
return id;
}

public String getName() {
return name;
}

// 你可以发现,在上面三个函数中包含大量的重复代码。
// 请尝试通过Predicate接口将上述代码抽取成一个公用的过滤器函数
// 并简化上面三个函数
public static List<User> filter(List<User> users, Predicate<User> predicate) {
List<User> results = new ArrayList<>();
for (User user: users) {
if(predicate.test(user)){
results.add(user);
}
}
return results;
}

public static void main(String[] args) {
List<User> res = filterUsersWithEvenId(Arrays.asList(new User(1,"a"),new User(2,"b")));
System.out.println(res);

List<User> res2 = filterZhangUsers(Arrays.asList(new User(1,"张三"),new User(2,"李四")));
System.out.println(res2);
}

// 过滤ID为偶数的用户
public static List<User> filterUsersWithEvenId(List<User> users) {
List<User> results = new ArrayList<>();
return filter(users, new Predicate<User>() {
@Override
public boolean test(User user) {
return user.id % 2 == 0 ;
}
});
}

// 过滤姓张的用户 lambda 表达式
public static List<User> filterZhangUsers(List<User> users) {
return filter(users, user -> user.name.startsWith("张"));
}

// 过滤姓王的用户
public static List<User> filterWangUsers(List<User> users) {
return filter(users, user -> user.name.startsWith("王"));
}
}

内部类详解

内部类

  • 用途:实现更加精细的封装
  • 可以访问外围类的实例方法
  • 非静态内部类
    • 和一个外围类实例相绑定
    • 可以访问外围类实例的方法
  • 静态内部类
    • 不和外围类实例绑定
    • 不可以访问外围实例的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Home {
void log(){
System.out.println(111);
}

public static void main(String[] args) {
new B(new Home()).xx();
}
static class B{

Home home;
// 通过构造器把外围类的实例注入到内部类来,使得静态内部类可以调用外围类的实例方法
B(Home home){
this.home = home;
}
void xx(){
home.log();
}
}

class C{
//编译器偷偷帮你注入一个 外围类的实例/对象
// private Home this$0;
// 这就是为什么非静态的内部类可以访问外部类的秘密所在
{
log();
}
}
}

静态内部类和非静态内部类使用的原则

  • 永远使用静态内部类,否则编译报错
    • 因为使用非静态内部类,编译器偷偷帮你注入一个 this$0的外围类实例,这个对象会占用空间的,如果不用,就浪费类空间

匿名内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Home {
public static void main(String[] args) {
// 匿名内部类
new Predicate<Object>(){
@Override
public boolean test(Object o) {
return true;
}
};
}
}

// 匿名相当于
class XXXX implements Predicate<Object>{

@Override
public boolean test(Object o) {
return false;
}
}
  • 好处就是非常的短小,逻辑紧密结合
  • 可以访问外围类的成员
  • 还可以变成 lambda 表达式

匿名类最后会变成什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Home {
public static void main(String[] args) {
// 匿名内部类
new Predicate<Object>(){
@Override
public boolean test(Object o) {
return true;
}
};

new Predicate<Object>(){
@Override
public boolean test(Object o) {
return true;
}
};

new Predicate<Object>(){
@Override
public boolean test(Object o) {
return true;
}
};
}
}

// 此时三个匿名内部类就会变成

Home$1.class
Home$2.class
Home$3.class