views:

76

answers:

2

I have code that looks like this:

public class A
{
    public void doStuff()
    {
        System.out.print("Stuff successfully done");
    }
}

public class B extends A
{
    public void doStuff()
    {
        System.out.print("Stuff successfully done, but in a different way");
    }

    public void doMoreStuff()
    {

        System.out.print("More advanced stuff successully done");
    }
}

public class AWrapper
{
    public A member;

    public AWrapper(A member)
    {
        this.member = member;
    }

    public void doStuffWithMember()
    {
        a.doStuff();
    }
}

public class BWrapper extends AWrapper
{
    public B member;

    public BWrapper(B member)
    {
        super(member);         //Pointer to member stored in two places:
        this.member = member;  //Not great if one changes, but the other does not

    }

    public void doStuffWithMember()
    {
        member.doMoreStuff();
    }
}

However, there is a problem with this code. I'm storing a reference to the member in two places, but if one changes and the other does not, there could be trouble. I know that in Java, an inherited method can narrow down its return type (and perhaps arguments, but I'm not certain) to a derived class. Is the same true of fields?

+3  A: 

You can accomplish this better with generics.

public class AWrapper<T extends A>
{
    public T member;
    public AWrapper(T member)
    {
        this.member = member;
    }

    public void doStuffWithMember()
    {
        a.doStuff();
    }
}    

public class BWrapper extends Wrapper<B>
{
    public BWrapper(B member)
    {
        super(member);
    }

    public void doStuffWithMember()
    {
        member.doMoreStuff();
    }
}

The fact that the subclass wrapper specifies the type of B allows you to access B's functions in the BWrapper, without storing an additional reference.

Jherico
Not necessarily. You can genericize the wrapper as many layers deep as you want, but if you're doing this kind of coding you're probably modeling the domain wrong.
Jherico
Ah sorry, I confused with my past expereince where I didn't want to genericize second level class for readability issues. Yes, one is willing to make `BWrapper` generic it will work fine.
doublep
What does this syntax mean? `public <T> class`? I've only ever seem type parameters _after_ identifiers
Eric
@Eric I was confusing the syntax for static method declarations of generic types with the class declaration format. Edited to fix.
Jherico
Yeah, but I've still seen that syntax elsewhere on SO. Is it ever appropriate, or is it just a common mistake?
Eric
A: 

In your class BWrapper you have to remove the line public B member;. And in the method doMoreStuffWithMember() replace the line with:

((B) member).doMoreStuff();
Martijn Courteaux
If you have to cast, you're doing it wrong.
Jherico
@Jherico: But in this case you are sure it is an instance of `B`.
Martijn Courteaux
@Martijn: You cannot be sure as written here, because `AWrapper.member` is `public` and could be set to a non-`B` instance. You would not even be safe if the attribute was `protected`, because some subclass of `BWrapper` could set the attribute.
Christian Semrau