1

I'm having trouble understanding inheritance. In the code below, why doesn't the inherited method access the field in the subclass? Is there any way to access the subclass field without overriding the inherited method?

class Fish {
    private String fishType = "Fish";
    public String getFishType() {
        return fishType;
    }
}

class Marlin extends Fish {
    private String fishType = "Marlin";
}

public class InheritanceTest {
    public static void main(String[] args) {
        Fish fish1 = new Fish();
        Fish marlin1 = new Marlin();
        System.out.println(fish1.getFishType());
        System.out.println(marlin1.getFishType());
    }
}

This code prints

Fish
Fish

but I was expecting

Fish
Marlin

Everyone seems to be answering based on the strings being private, yet even if I change the fields to public I still have the problem. The question isn't about inheriting private fields.

Please see updated code below.

class Fish {
    public String fishType = "Fish";
    public String getFishType() {
        return fishType;
    }
}

class Marlin extends Fish {
    public String fishType = "Marlin";
}

public class InheritanceTest {
    public static void main(String[] args) {
        Fish fish1 = new Fish();
        Marlin marlin1 = new Marlin();
        System.out.println(fish1.getFishType());
        System.out.println(marlin1.getFishType());
    }
}

The output and my expectations are the same as above.

Makoto
  • 104,088
  • 27
  • 192
  • 230
JG1212
  • 52
  • 10
  • Why is `fishType` not `final static`? Or just an inline String inside of the getter (that would be less ambiguous here). Can it change? – Thilo Jun 17 '15 at 03:57
  • You changed it from private to package-private (which is quite similar). Try `protected` or `public` and you will get a different result (namely a compile-error about duplicate fields). – Thilo Jun 17 '15 at 04:28
  • @Thilo I tried both protected and public and got the same exact results. Fish – JG1212 Jun 17 '15 at 04:36

4 Answers4

5

First, fields (and methods) that are private are not inherited. Your Marlin class doesn't even know of its parent field.

Second, Java will pick the most appropriate method to call based on the instance you're using at runtime. Since Marlin doesn't have the method getFishType defined, it will use its parents' method.

There are several approaches you can take to this; one of which is overriding the getFishType method inside of Marlin:

@Override
public String getFishType() {
    return fishType;
}

With the new code, you're actually hiding the variable by redeclaring it inside of Marlin.

If the class declares a field with a certain name, then the declaration of that field is said to hide any and all accessible declarations of fields with the same name in superclasses, and superinterfaces of the class.

That's easily solvable by not redeclaring the variable; rather, assign the value you want at construction time.

public Marlin() {
    fishType = "Marlin";
}

Alternatively, in a bid to keep the code cleaner, you can restructure your classes in a way that you make use of the parent's method as opposed to overriding it in the child. This does require a couple of constructors in the parent class.

class Fish {
    private final String fishType;

    // Default constructor; used in nominal cases
    public Fish() {
        this("Fish");
    }

    // Constructor used to populate the fishType field
    public Fish(final String fishType) {
        this.fishType = fishType;
    }

    public String getFishType() {
        return fishType;
    }
}

class Marlin extends Fish {
    public Marlin() {
        super("Marlin"); // invoke super's constructor
    }
}

Once again, since there's no suitable method getFishType in Marlin, it will look to the Fish class and use its method instead. This time, however, the value we actually want to come through is the one we expect.

Makoto
  • 104,088
  • 27
  • 192
  • 230
  • @Makoto the 2nd part of your answer seems to be the clearest. Not sure why it is -1. But I guess I still don't understand why `Marlin` doesn't have the `getFishType` method. I thought it was inherited from the `Fish` class. – JG1212 Jun 17 '15 at 04:22
  • It is inherited; it's just not making use of the same field in the child as the parent. I can show you an example that would work without the need to override the method, but it'd require changing around a few things about the structure of the class. – Makoto Jun 17 '15 at 04:24
  • That alternative solution looks best. – Thilo Jun 17 '15 at 04:39
  • @Makoto Thank you for the alternate solution. Any chance you could expand on "It is inherited; it's just not making use of the same field in the child as the parent."? Or point me in the right direction? I can't seem to find anything that explains why the inherited method is only seeing the superclass's fields and not the class I am calling it from. Thanks. – JG1212 Jun 17 '15 at 04:53
  • I've went a head and revised the answer a bit to include some information that takes your newer code into account. Note that again the private field isn't acknowledged inside of the child class; this is due to the way the `private` visibility modifier works. This I must regrettably leave as an exercise for the reader. – Makoto Jun 17 '15 at 05:02
  • @Makoto, thank you for all the help. I added another edit to my question, showing my code with the constructors. I would upvote you if I could but I only have a reputation of 6 ;) I marked it as answered. – JG1212 Jun 17 '15 at 05:36
  • I'm glad that I was able to give you a hand, but it's not really necessary to edit your question with that. The answers are meant to capture the ultimate result. – Makoto Jun 17 '15 at 07:28
1

you have to override getFishType() in Marlin class. private property is only visible within a class.

1

Although a fair answer by @Makoto I feel the OP question's intend is not properly answered.

The question is about why the re-declared fishType in the Marlin class is not honored by the inherited getFishType() method when called on an Marlin instance.

This is even more curious when you think of how differently method lookup works (as opposed to field lookup): If you added e.g. a method Fish::getT():protected String getT() {return "Fish";} and overrode it in Marlin::getT() with protected String getT() {return "Marlin";} and called this new method in Fish::getFishType() instead of accessing the field, the call to marlin1.getFishType() would actually produce "Marlin". - Here, the subclass method is used in the inherited method, not the parent method of the same name. This is because method lookup always starts at the current instance.

I think the answer is indicated by the quote from the Java docs given by @Makoto: The re-declared field "hides" all other accessible declarations of the same name in the inheritance hierarchy (whereas a re-declared method "overrides" previous declarations of the same name and signature). I take this to mean that the Marlin.fishType has to be considered like a completely different symbol from Fish.fishType (with accidentally the same name). And Fish.getFishType() just sees its own version of it.

ThomasH
  • 22,276
  • 13
  • 61
  • 62
0

You should override getFishType(), not just fishType. The method is inherited but the private properties are not.

Corwin Newall
  • 508
  • 8
  • 18