如何避免java中的多重继承

如何避免java中的多重继承,java,multiple-inheritance,Java,Multiple Inheritance,我正处于这样一种情况,我试图实现一个(相对简单的)抽象语法树。所有节点都从一个名为SimpleNode的类型继承,该类型包含一些用于存储行和列信息并接受访问者的代码 现在,一些节点也应该是可命名的,而另一些节点应该具有“可访问”属性(例如public或private)。有些节点甚至应该支持这两个接口 我最好使用虚拟继承实现这一点,并编写两个类NameableNode和AccessibleNode,但Java不支持MI 例如NameableNode可能有字段“name”,并为该字段实现简单的get

我正处于这样一种情况,我试图实现一个(相对简单的)抽象语法树。所有节点都从一个名为SimpleNode的类型继承,该类型包含一些用于存储行和列信息并接受访问者的代码

现在,一些节点也应该是可命名的,而另一些节点应该具有“可访问”属性(例如public或private)。有些节点甚至应该支持这两个接口

我最好使用虚拟继承实现这一点,并编写两个类NameableNode和AccessibleNode,但Java不支持MI

例如NameableNode可能有字段“name”,并为该字段实现简单的getter和setter。类似地,AccessibleNode也可能有一个字段“accessibility”和getter/setter

实现这一点并避免在大量代码库中引入代码重复的好方法是什么

小代码示例:

public class SimpleNode {
    private int line = 0;
    private int column = 0;

    /* Getters and setters for line/column. */
    /* ... */
}

public class NameableNode extends SimpleNode {
    private String name = "";

    /* Getters and setters for name */
}

public class AccessibleNode extends SimpleNode {
    private boolean isPublic = false;

    /* Getters and setters for accessibility */
}

在这种情况下,我将使用组合方法而不是继承:

public class Node {
    private int line = 0;
    private int column = 0;

    /* Getters and setters for line/column. */
    /* ... */

    private String name = null;
    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this._name = name;
    }    

    private Boolean _isPublic = null;
    public String isPublic() {
        return this.name;
    }

    public void setIsPublic(boolean isPublic) {
        this._isPublic = isPublic;
    }

    public boolean hasAccessibility() {
        return this._isPublic != null;
    }

    public boolean hasName() {
        return this._name != null;
    }
}
我更喜欢的另一个解决方案是使用HashMap和指示节点所有可能属性的枚举动态创建这些属性。这种方法更通用,因为它需要编写更少的代码来支持新属性,但它也不太安全,因为需要在运行时强制转换其他属性:

import java.util.HashMap;

enum NodeAttribute {
  NAME,
  ACCESSIBILTY
}

enum NodeAccessibility {
  PUBLIC,
  PRIVATE
}

public class Node {
    private int line = 0;
    private int column = 0;

    // Notice that this Object usage might involve some boxing for attributes of premitive type 
    private HashMap<NodeAttribute, Object> additionalAttributes = new HashMap<NodeAttribute, Object>();

    /* Getters and setters for line/column. */
    /* ... */

    public boolean hetAttribute(NodeAttribute attribute) {
        return this.additionalAttributes.containsKey(attribute);        
    }

    public <T> T getAttributeValue(NodeAttribute attribute, Class<T> attributeClass) {
        Object attributeValue = this.additionalAttributes.get(attribute);

        // You may want to wrap the ClassCastException that may be raisen here to a more specfic error 
        T castedAttributeValue = attributeClass.cast(attributeValue);
        return castedAttributeValue;
    }

    public void setAttributeValue(NodeAttribute attribute, Object value) {
        // Notice that this implemintation allows changing the type of an existing attribute,
        // If this is invalid behavior in your case you can throw an exception instead
        this.additionalAttributes.put(attribute, value);        
    }
}

// Example usage
public class Program {
    public static void main(String[] args) {
        Node nodeWithNameOnly = new Node();        
        nodeWithNameOnly.setAttributeValue(NodeAttribute.NAME, 'node1');

        Node nodeWithBoth = new Node();
        nodeWithBoth.setAttributeValue(NodeAttribute.NAME, 'node2');
        nodeWithBoth.setAttributeValue(NodeAttribute.ACCESSIBILTY, NodeAccessibility.PRIVATE);

        Program.doStuffWithNode(nodeWithNameOnly);
        /* output:
            Node name: node1
        */
        Program.doStuffWithNode(nodeWithBoth);
        /* output:
            Node name: node2
            Node is public: False
        */
    }

    public static void doStuffWithNode(Node node) {
        if (nodeWithNameOnly.hetAttribute(NodeAttribute.NAME)) {
            String nodeName = nodeWithNameOnly.getAttributeValue(NodeAttribute.NAME, String.class);
            system.out.println("Node name: " + nodeName);
        }

        if (nodeWithNameOnly.hetAttribute(NodeAttribute.ACCESSIBILTY)) {
            NodeAccessibility nodeAccessibilty =
                nodeWithNameOnly.getAttributeValue(NodeAttribute.ACCESSIBILTY, NodeAccessibility.class);
            boolean nodeIsPublic = nodeAccessibilty == NodeAccessibility.PUBLIC;
            system.out.println("Node is public: " + String.valueOf(nodeIsPublic));
        }
    }
}
import java.util.HashMap;
枚举节点属性{
名称
易接近性
}
枚举节点可访问性{
公众,,
私有的
}
公共类节点{
私有整数行=0;
私有int列=0;
//请注意,此对象用法可能涉及一些前置类型属性的装箱
私有HashMap additionalAttributes=新HashMap();
/*行/列的getter和setter*/
/* ... */
公共布尔属性(NodeAttribute属性){
返回this.additionalAttributes.containsKey(属性);
}
公共T getAttributeValue(NodeAttribute属性,类attributeClass){
Object attributeValue=this.additionalAttributes.get(属性);
//您可能希望将这里可能出现的ClassCastException包装为更具体的错误
T castedAttributeValue=attributeClass.cast(attributeValue);
返回castedAttributeValue;
}
public void setAttributeValue(NodeAttribute属性,对象值){
//请注意,此实现允许更改现有属性的类型,
//如果这在您的案例中是无效的行为,您可以抛出异常
this.additionalAttributes.put(属性,值);
}
}
//示例用法
公共课程{
公共静态void main(字符串[]args){
Node nodeWithNameOnly=新节点();
setAttributeValue(NodeAttribute.NAME,'node1');
Node nodeWithBoth=新节点();
setAttributeValue(NodeAttribute.NAME,'node2');
setAttributeValue(NodeAttribute.Accessibility、NodeAccessibility.PRIVATE);
Program.dostufwithnode(仅限nodeWithName);
/*输出:
节点名称:node1
*/
Program.doStuffWithNode(nodeWithBoth);
/*输出:
节点名称:node2
节点是公共的:False
*/
}
公共静态void doStuffWithNode(节点节点){
if(nodeWithNameOnly.hetAttribute(NodeAttribute.NAME)){
String nodeName=nodeWithNameOnly.getAttributeValue(NodeAttribute.NAME,String.class);
system.out.println(“节点名称:“+nodeName”);
}
if(nodeWithNameOnly.hetAttribute(NodeAttribute.ACCESSIBILTY)){
节点可访问性节点可访问性=
nodeWithNameOnly.getAttributeValue(NodeAttribute.Accessibility,NodeAccessibility.class);
布尔值nodeIsPublic=nodeAccessibilty==NodeAccessibility.PUBLIC;
system.out.println(“节点是公共的:+String.valueOf(nodeIsPublic));
}
}
}
在任何情况下,这是主要的经验法则——继承应该用于“是一个”关系,而组合应该用于“有一个”关系

例如:

  • 延伸到动物,因为鱼是动物
  • Post保留评论,因为Post有评论
在我们的例子中,一个节点有一个名称和一个可访问性级别,所以它应该包含它们。

您正在寻找的。这有很多种风格——根据我对您试图构建的内容的理解,我将提出一种适合您的目的的风格

首先,让我们为您的
节点
s创建一些接口:


可命名的公共接口{
/*名称的getter和setter*/
}
公共接口可访问{
/*可访问性的getter和setter*/
}
接下来,您可能不想对每个
节点重复相同的实现,因此让我们创建这些实现:


公共类NameDelegate(){
私有字符串名称=”;
/*名称的getter和setter*/
}
公共类AccessDelegate(){
私有布尔值isPublic=false;
/*可访问性的getter和setter*/
}
现在,让我们把一切都放在一起:

public类SomeNodeA扩展SimpleNode实现了Nameable{
私人姓名代表姓名代表;
公共SomeNodeA(NameDelegate NameDelegate){
this.nameDelegate=nameDelegate;
}
@凌驾
公共字符串getName(){
返回nameDelegate.getName();
}
@凌驾
公共字符串集合名(字符串名){
nameDelegate.setName(名称);
}
}
您也可以在一个类中同时具有这两种行为:

公共类SomeNodeB扩展了SimpleNode,实现了可命名、可访问的{
私人姓名代表姓名代表;
私有访问代理访问代理;
公共SomeNodeB(NameDelegate NameDelegate,AccessDelegate AccessDelegate