简体   繁体   中英

Delegating methods to subclasses in Java

I have a superclass Shape, and classes Triangle, Square, etc. extend Shape. I have two current issues:

  1. My method Triangle extends Shape does not compile. It has to return a Shape, not a Triangle.
  2. I want to hide this method. It should only be callable from the Shape superclass.
public class Shape {
  public static Shape createShapeFromXML(String xml) {
    String type = parse(xml);
      if (type.equals("Triangle") {
        Triangle.createShapeFromXML(xml);
      } else if (...) {
      // ...
    }
  }
}

public class Triangle extends Shape {
  public static Triangle createShapeFromXML(String xml) {
    ....
  }
}

public static void main(String[] args) {
  String xml = ...
  Shape s = Shape.createShapeFromXML(xml);
}

How can I resolve these issues?

why don't you keep the one static method in the superclass, and have it return the appropriate Shape subclass? The signature would stay the same because Triangles have an is-a relationship to Shape.

You could make the method on the superclass private to get the access restriction you want...

Another approach would be to use the Factory pattern. You could have a ShapeFactory... This is a good idea because creating the xml parsing is not a concern of the Shape classes. Separate your concerns. The wikipedia link is good at describing the pattern, but you might want a simpler example. See this .

// 2. I want to hide this method. It should only be callable from superclass Shape

You can make the Shape method final in order to lock down the implementation. Even your overloaded method that returns a subclass type ( Triangle in your example) would be flagged by the compiler.

public static final Shape createShapeFromXML(String xml) { ... }

EDIT:

in response to the conversation in the comments, for evidence I provide the following:

public class Shape {
   public static final Shape createShapeFromXML(String xml) {
      if (xml.equals("Triangle")) {//removed parse for demo compliation
         return Triangle.createShapeFromXML(xml);
      } else {
         return new Shape();
      }
   }
}

public class Triangle extends Shape{
   public static Triangle createShapeFromXML(String xml) {
      return new Triangle();
   }
} 

trying to compile the above will result in a compiler error:

mybox:src akf$ javac Triangle.java
Triangle.java:3: createShapeFromXML(java.lang.String) in Triangle cannot override createShapeFromXML(java.lang.String) in Shape; overridden method is static final
     public static Triangle createShapeFromXML(String xml) {
                                ^
1 error

This can be explained using the JLS by referencing two sections:

from 8.4.6.2 Hiding (by Class Methods) :

If a class declares a static method, then the declaration of that method is said to hide any and all methods with the same signature in the superclasses and superinterfaces of the class that would otherwise be accessible to code in the class.

and then from 8.4.3.3 final Methods :

A method can be declared final to prevent subclasses from overriding or hiding it. It is a compile-time error to attempt to override or hide a final method.

Putting the two together, adding final to the signature of a static method will protect that method from being hidden by subclasses. It will enforce compile-time checking.

To make your code compile you need to declare public static Shape createShapeFromXML(String xml) in the Triangle class.


public class Shape {

    public static void main(String[] args) {
            String xml = "Triangle";
            Shape s = Shape.createShapeFromXML(xml);
            System.out.println(s.toString());
    }

    public static Shape createShapeFromXML(String xml) {
       Shape aShape = null;

       if (xml.equals("Triangle")) {
         aShape = Triangle.createShapeFromXML(xml);
       }
       return aShape;
     }
 }

class Triangle extends Shape {

    public static Shape createShapeFromXML(String xml) {
         return new Triangle();
    }

    @Override
    public String toString() {
       return "Triangle";
    }
 }

The System.out.println(s.toString()); in the main method outputs "Triangle", this proves that a Triangle shape is being created.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM