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
相关推荐
不才不才不不才1 小时前
Spring 源码系列(16): doDispatch 全流程——一次请求的主干链路
java·后端·spring
snow@li1 小时前
HikariCP:高性能数据库连接池全景深入梳理
java·数据库
edwarddamon2 小时前
Spring Cloud 配置热更新与 Bean 代理机制梳理
java·后端
Raas1002 小时前
MAIGateway,魔芋企业级AI网关的FinAPI成本竞争力设计
java·后端·网关·api网关·魔芋ai·finapi·mai gateway
(Charon)2 小时前
【C++】锁与原子操作(四):自旋锁的完整实现与性能优化
c语言·开发语言·c++
雨田言炎3 小时前
五、Qt控件使用说明大全
开发语言·qt
evans在进步3 小时前
Spring AI 从入门到实战:用 Java 实现大模型对话与 Tool Calling
java·人工智能·spring
evans在进步3 小时前
LeetCode 189 轮转数组:三次反转原地解决,图解 Java 实现
java·算法·leetcode
bu_shuo4 小时前
MATLAB中的meshgrid和ndgrid
开发语言·matlab