Home > database >  Java extend and get inner class
Java extend and get inner class

Time:09-02

I am extending a class and also extending it's inner class but I'm getting errors trying to get the inner class through the outer child class.

public class SMONArray extends SMONArr {

    public class Element extends SMONArr.Element {
        protected int x, y, w, h;

        protected Element(String string, Object arr) {
            super(string, arr);
        }
    }

    public Element getSelected(String key, int index) {
        return get(key, index);
    }
}

public class SMONArr {
    protected final LinkedHashMap<String, ArrayList<Element>> arr = new LinkedHashMap<>();
    public class Element {
        public String key;
        public Object elem;

        protected Element(String string, Object arr) {
            key = string;
            elem = arr;
        }
    }

    public Element get(String key, int index) {
        return arr.get(key).get(index);
    }
}

Like it is above I'm getting an error in the SMONArray child class getSelected function which is:

Type mismatch: cannot convert from SMONArr.Element to SMONArray.Element

Intellij suggests to cast the returned value to Element like this:

public Element getSelected(String key, int index) {
            return (Element) get(key, index);

But like this the error goes away and I can run the program, but when I call the getSelected function I get a different error:

Exception in thread "main" java.lang.ClassCastException: SMONArr$Element cannot be cast to SMONArray$Element

If I don't extend the inner class I have no problems but if I do, how can I then get the inner class through the outer child class?

EDIT: The array arr in SMONArr is populated with ArrayList of Element of which Element is a String key and an int[] e.g. key, [1, 2, 3, 4] is what Element is.

intArr[j] = Integer.parseInt(strArr[j]);
linesArr.add(new Element(parts[0].trim(), intArr));
arr.put(key, linesArr);

CodePudding user response:

Add a createElement class to your parent class:

public class SMONArr {
  // ...

  // Alternatively, this can be abstract (assuming you make the class abstract too)
  // then it has to be overridden in subclasses.
  public Element createElement(String key, Object value) {
    return new Element(key, value);
  }

  // ...
}

then you can override this in the subclass:

public class SMONArray extends SMONArr {
  // ...

  // Now Element refers to the subclass.
  public Element createElement(String key, Object value) {
    return new Element(key, value);
  }

  // ...
}

and invoke createElement instead of new Element in the parent class. Then the cast in the subclass will work without a ClassCastException.


You can do this without the need for the getSelected method in subclasses, by making the SMONArr class generic:

public abstract class SMONArr<E extends Element> {
  protected final LinkedHashMap<String, ArrayList<E>> arr = new LinkedHashMap<>();

  public class Element { ... }

  // ...

  // Now this has to be abstract.
  public abstract E createElement(String key, Object value);

  // ...
}

public class SMONArray extends SMONArr<SMONArray.Element> {
  public class Element extends SMONArr.Element { ... }

  public Element createElement(String key, Object value) {
    return new Element(key, value);
  }
}

CodePudding user response:

You have two element types. You have a collection in the SMONArr class that can only hold one of the types.

The call to 'get' in SMONArr class returns a SMONArr.Element, which is not a SMONArray.Element.

I don't know the context - perhaps in this case the collection is guaranteed to hold only SMONArray.Elements, in which case a cast would do the trick.

CodePudding user response:

Your issue has nothing to do with extending the inner class, you return the wrong type and don't see it because of the naming confusion.

This is what you're doing:

public class Outer1 {
 class Inner1 {}

 public Inner1 get() {
  return new Inner1();
 }
}
public class Outer2 extends Outer1 {
    public class Inner2 extends Outer1.Inner1 {}

    public Inner2 getSelected() {
        return get();
    }
}

You getSelected() is declared as returning the subtype, but then you call the method returning the supertype.

You can change its return type to Inner1, or in your case SMONArr.Element.

public SMONArr.Element getSelected(String key, int index) {
  return get(key, index);
}
  • Related