Java中级——组合和继承

组合和继承

这是两种不同的实现方式,不能混用

java 复制代码
public class GetterErrorDemo {

    private static final String TAG = GetterErrorDemo.class.getSimpleName();

    public static class Node {
        private final String name;

        public Node(String name) {
            this.name = name;
        }

        @NonNull
        @Override
        public String toString() {
            return "Node{" + name + '}';
        }
    }

    public static class BaseInfo {
        private Node mNode;

        public Node getNode() {
            return mNode;
        }

        public void setNode(Node node) {
            this.mNode = node;
        }

        @NonNull
        @Override
        public String toString() {
            // 关键点:打印的是字段 mNode,而不是 getNode()
            return "BaseViewInfo{node=" + mNode + '}';
        }
    }

    public static class ChildInfo extends BaseInfo {
        private final BaseInfo origin;
        private String mLabel; // child 自己的属性

        public ChildInfo(BaseInfo origin) {
            this.origin = origin;
        }

        @Override
        public Node getNode() {
            return origin.getNode(); // getter 重写,委托给原始控件
        }

        public void setLabel(String label) {
            this.mLabel = label;
        }

        @NonNull
        @Override
        public String toString() {
            // 父类字段 mNode 从没被赋值 → 打印出 null,但 getNode() 却非空
            return "ChildInfo{" + super.toString() + ", label=" + mLabel + '}';
        }
    }

    public static void demo() {
        BaseInfo origin = new BaseInfo();
        origin.setNode(new Node("node1"));
        ChildInfo child = new ChildInfo(origin);
        child.setLabel("我是child自己的属性");

        ArrayList<BaseInfo> list = new ArrayList<>();
        list.add(origin);
        list.add(child);
        for (BaseInfo baseInfo : list) {
            Log.d(TAG, "info = " + baseInfo);
        }
        origin.setNode(new Node("node2"));
        Log.d(TAG, "--- after origin.setNode(node2) ---");
        for (BaseInfo baseInfo : list) {
            Log.d(TAG, "info = " + baseInfo);
        }

        Log.d(TAG, "origin.getNode().equals(child.getNode()) = " + origin.getNode().equals(child.getNode()));
    }
}

对于如上代码,ChildInfo即继承了BaseInfo,又组合了BaseInfo,导致toString打印的mNode始终为空,而重写的getNode()又不为空,导致增加排查难度

复制代码
info = BaseInfo{node=Node{node1}}
info = ChildInfo{BaseInfo{node=null}, label=我是child自己的属性}
--- after origin.setNode(node2) ---
info = BaseInfo{node=Node{node2}}
info = ChildInfo{BaseInfo{node=null}, label=我是child自己的属性}
origin.getNode().equals(child.getNode()) = true

继承(不共有域)

java 复制代码
public class GetterCopyDemo {

    private static final String TAG = GetterCopyDemo.class.getSimpleName();

    public static class Node {
        private final String name;

        public Node(String name) {
            this.name = name;
        }

        @NonNull
        @Override
        public String toString() {
            return "Node{" + name + '}';
        }
    }

    public static class BaseInfo {
        private Node mNode;

        public BaseInfo() {
        }

        // 拷贝构造:以后新增字段只改这里一处
        public BaseInfo(BaseInfo other) {
            this.mNode = other.mNode;
        }

        public Node getNode() {
            return mNode;
        }

        public void setNode(Node node) {
            this.mNode = node;
        }

        @NonNull
        @Override
        public String toString() {
            return "BaseInfo{node=" + mNode + '}';
        }
    }

    public static class ChildInfo extends BaseInfo {
        private String mLabel; // child 自己的属性

        public ChildInfo(BaseInfo origin) {
            super(origin); // 一行复制 origin 的所有字段
        }

        public void setLabel(String label) {
            this.mLabel = label;
        }

        @NonNull
        @Override
        public String toString() {
            return "ChildInfo{" + super.toString() + ", label=" + mLabel + '}';
        }
    }

    public static void demo() {
        Log.d(TAG, "------------------------------------------------");
        BaseInfo origin = new BaseInfo();
        origin.setNode(new Node("node1"));

        ChildInfo child = new ChildInfo(origin);
        child.setLabel("我是child自己的属性");

        ArrayList<BaseInfo> list = new ArrayList<>();
        list.add(origin);
        list.add(child);
        for (BaseInfo baseInfo : list) {
            Log.d(TAG, "info = " + baseInfo);
        }

        // child 是独立副本:改 origin 不会影响 child
        origin.setNode(new Node("node2"));
        Log.d(TAG, "--- after origin.setNode(node2) ---");
        for (BaseInfo baseInfo : list) {
            Log.d(TAG, "info = " + baseInfo);
        }
    }
}

对于如上代码,ChildInfo构造函数调用BaseInfo的拷贝构造,同时toString打印父类字段和子类字段,避免了上面的打印和实际值不一样的问题

复制代码
info = BaseInfo{node=Node{node1}}
info = ChildInfo{BaseInfo{node=Node{node1}}, label=我是child自己的属性}
--- after origin.setNode(node2) ---
info = BaseInfo{node=Node{node2}}
info = ChildInfo{BaseInfo{node=Node{node1}}, label=我是child自己的属性}
origin.getNode().equals(child.getNode()) = false

组合(共有域)

复制代码
public class GetterDelegateDemo {

    private static final String TAG = GetterDelegateDemo.class.getSimpleName();

    public static class Node {
        private final String name;

        public Node(String name) {
            this.name = name;
        }

        @NonNull
        @Override
        public String toString() {
            return "Node{" + name + '}';
        }
    }

    public interface ViewInfo {
        Node getNode();
    }

    public static class BaseInfo implements ViewInfo {
        private Node mNode;

        @Override
        public Node getNode() {
            return mNode;
        }

        public void setNode(Node node) {
            this.mNode = node;
        }

        @NonNull
        @Override
        public String toString() {
            return "BaseInfo{node=" + mNode + '}';
        }
    }

    public static class ChildInfo implements ViewInfo {
        private final ViewInfo origin; // 父类那部分委托 origin,自己不存
        private String mLabel;         // child 自己新增的属性,自己存

        public ChildInfo(ViewInfo origin) {
            this.origin = origin;
        }

        @Override
        public Node getNode() {
            return origin.getNode(); // 委托 origin
        }

        public String getLabel() {
            return mLabel; // 自己的属性走自己的字段
        }

        public void setLabel(String label) {
            this.mLabel = label;
        }

        @NonNull
        @Override
        public String toString() {
            return "ChildInfo{origin=" + origin + ", label=" + mLabel + '}';
        }
    }

    public static void demo() {
        Log.d(TAG, "------------------------------------------------");
        BaseInfo origin = new BaseInfo();
        origin.setNode(new Node("node1"));

        ChildInfo child = new ChildInfo(origin);
        child.setLabel("我是child自己的属性");

        ArrayList<ViewInfo> list = new ArrayList<>();
        list.add(origin);
        list.add(child);
        for (ViewInfo baseInfo : list) {
            Log.d(TAG, "info = " + baseInfo);
        }

        // child 是委托:改 origin 会影响 child
        origin.setNode(new Node("node2"));
        Log.d(TAG, "--- after origin.setNode(node2) ---");
        for (ViewInfo baseInfo : list) {
            Log.d(TAG, "info = " + baseInfo);
        }
        Log.d(TAG, "origin.getNode().equals(child.getNode()) = " + origin.getNode().equals(child.getNode()));
    }
}

对于如上代码,ChildInfo持有BaseInfo,将父类字段给BaseInfo,并抽象ViewInfo用于遍历,同时toString打印父类字段和子类字段,避免了上面的打印和实际值不一样的问题

复制代码
info = BaseInfo{node=Node{node1}}
info = ChildInfo{origin=BaseInfo{node=Node{node1}}, label=我是child自己的属性}
--- after origin.setNode(node2) ---
info = BaseInfo{node=Node{node2}}
info = ChildInfo{origin=BaseInfo{node=Node{node2}}, label=我是child自己的属性}
origin.getNode().equals(child.getNode()) = true
相关推荐
小成很成9 小时前
mysql 8.0.21 升级 mysql 8.0.45(经过生产环境验证)
android·mysql·adb
「QT(C++)开发工程师」10 小时前
C++ 11 常用for循环
开发语言·c++
我还记得那天10 小时前
0 初识C++
开发语言·c++
FfHUCisI10 小时前
Go 编译过程全景
开发语言·后端·golang
FfHUCisI10 小时前
Golang 语法分析与 AST:Parser 与 go/ast
开发语言·后端·golang
秋饼11 小时前
JDK 27 企业级 AI 服务落地实战:G1 默认、紧凑对象头、后量子 TLS 与 JFR 脱敏全解析
java·ai·技术分享·后端开发
卓怡学长11 小时前
w176基于SpringBoot的医院管理系统
java·spring boot·spring·maven·intellij-idea
lemon_sjdk11 小时前
ObjectProperty
java·开发语言·算法
大模型码小白11 小时前
Spring AI Tool 实现自然语言操作 MySQL 数据库详解
服务器·开发语言·数据库·人工智能·python·mysql·spring