Home > Software engineering >  java - class attribute type change with inheritance
java - class attribute type change with inheritance

Time:06-28

I have a MyClass which has an attribute of type MyAttribute. This class is inherited by MySubClass which has an attribute of type MySubAttribute. MySubAttribute is a subclass of MyAttribute :

class MyClass {
    MyAttribute myAttribute;
    MyClass(MyAttribute myAttribute) {
        this.myAttribute = myAttribute;
    }

    MyAttribute getMyAttribute() {
        return myAttribute;
    }
}

class MySubClass extends MyClass {
    MySubClass(MySubAttribute mySubAttribute) {
        super(mySubAttribute);
    }
}


class MyAttribute {
    void doSomething() {
    }
}

class MySubAttribute extends MyAttribute {
    @Override
    void doSomething() {
        super.doSomething();
    }
    void doSomethingElse() {
    }
}

Now imagine that I have the following code:

mySubClass.getMyAttribute();

How to make the returned value of type MySubAttribute?

CodePudding user response:

You could add something like this to MySubClass:

@Override
MySubAttribute getMyAttribute() {
    return new MySubAttribute();
}

CodePudding user response:

You should take a look at Java Generics.

Also, you no longer need to define MySubClass unless you wanna add extra methods or properties to it.

class MyClass<T extends MyAttribute> {
  T myAttribute;
  MyClass(T myAttribute) {
    this.myAttribute = myAttribute;
  }

  T getMyAttribute() {
    return myAttribute;
  }
}


class MyAttribute {
  void doSomething() {
    System.out.println("Doing something...");
  }
}

class MySubAttribute extends MyAttribute {
  void doSomethingElse() {
    System.out.println("Doing something else...");
  }
}

// You would instantiate `mySubClass` as follows
MyClass<MySubAttribute> mySubClass = new MyClass<>(new MySubAttribute());
mySubClass.getMyAttribute().doSomethingElse();

CodePudding user response:

The usual solution is generics:

class MyClass<A extends MyAttribute> {
    A myAttribute;
    MyClass(A myAttribute) {
        this.myAttribute = myAttribute;
    }

    A getMyAttribute() {
        return myAttribute;
    }
}

class MySubClass extends MyClass<MySubAttribute> {

Alternatively, you could have MySubClass override the getter with a narrower return type:

    @Override
    MySubAttribute getMyAttribute() {
        // we know the cast succeeds because we have set a MySubAttribute in the constructor
        return (MySubAttribute) myAttribute; 
    }

The generic solution offers better compile time checking of the implementing class, and allows a caller to refer to the type of the attribute even if they don't know the subtype of MyClass by writing MyClass<MySubAttribute>. On the other hand, the generic solution does require callers to write a type parameter (even if just MyClass<?>), so calling code gets slightly more verbose.

CodePudding user response:

If I understand correctly, then you're asking for:

public interface HasMyAttribute {
    MyAttribute getMyAttribute();
}

public class MyClass implements HasMyAttribute {
    private MyAttribute myAttribute;

    public MyClass(MyAttribute myAttribute) {
        this.myAttribute = myAttribute;
    }

    @Override
    public MyAttribute getMyAttribute() {
        return this.myAttribute;
    }
}

public class MySubClass extends MyClass implements HasMyAttribute {

    private MySubAttribute mySubAttribute;

    public MySubClass(MySubAttribute mySubAttribute) {
        super(mySubAttribute);
        this.mySubAttribute = mySubAttribute;
    }

    @Override
    public MySubAttribute getMyAttribute() {
        return mySubAttribute;
    }
}

(though, if you don't need two distinct concrete classes, @xxMrPHDxx's answer is much better)

  • Related