-5

How do I access the property of a child class instead of the abstract class, when handling objects by their parent/abstract class? Like in this example:

public class WordCategories {
  public static abstract class Noun {
    public static final String TYPE = null;
    //... and so on, methods
  }
  public static class Person extends Noun {
    public static final String TYPE = "Person";
    // ...
  }
}

/* ... then we build a collection with members like: */
nouns.add(new WordCategories.Person("Bill Clinton");

/* now later we need to access a mixed list of nouns: */
for(WordCategories.Noun n: nouns) {
  if(n.TYPE.equals("Person") ){ // this is always null
}

Obviously I could just specify (WordCategories.Person) n but that assumes that it is a Person. I need the cast to come from the parent class, and it would be most elegant if it did not involve interpreting the TYPE constant or the use of reflection, for that matter.

roberto tomás
  • 4,435
  • 5
  • 42
  • 71
  • the bot is wrong this is not a string comparison question – roberto tomás Apr 11 '16 at 03:04
  • 1
    @hovercraft I don't think so. This is about accessing static fields in child classes from a parent class expression. – Savior Apr 11 '16 at 03:04
  • 2
    @Pillar: perhaps, but his original post, before the "bot" comment and edit, showed obvious incorrect comparison of Strings. – Hovercraft Full Of Eels Apr 11 '16 at 03:05
  • yes I edited it.. thanks. I made the example on the spot. my acutal problem is that I am getting a null pointer reference by assuming the class variable is accessible .. and it *is* , I can see it in the debugger. but I can't access it when it is cast to the abstract class. – roberto tomás Apr 11 '16 at 03:06
  • @HovercraftFullOfEels perhaps, but they were comparing string constants, so they would've been equal, maybe intended. – Savior Apr 11 '16 at 03:07
  • If you want to determine an object's class, why not use `getClass()`? (And if the answer is "I don't want to use reflection", well... why not?) In any case, `static` fields are always looked up by static type, not runtime type. – user2357112 Apr 11 '16 at 03:08
  • @Pillar: fine -- I'll re-open. – Hovercraft Full Of Eels Apr 11 '16 at 03:08
  • @user2357112 I could do that .. but then I need to have a switch on the class type before casting so I can access the `TYPE` variable. It will always be the same name. I want to generify the process rather than building code-level copies of my object hierarchy that have to be kept in sync. – roberto tomás Apr 11 '16 at 03:11

3 Answers3

1

You don't have to use reflection at all. If you understand what type it is you like, you can use instanceof to get the specific class instance you care about.

for(WordCategories.Noun n: nouns) {
  if(n instanceof WordCategories.Person) {
      // cast to WordCategories.Person and perform whatever action you like
      WordCategoriesPerson actualPerson = (WordCategories.Person) n;
  }
}

This trumps the usage of the field to determine the object type, since the class contains enough metadata for you to want to use in this scenario. While many people would also discourage the use of instanceof due to performance (and frankly, if you wanted a list containing WordCategories.Person, just ask for one), its use in this instance would be cleaner than forcing each child class of WordCategories.Noun to create a method to inform us of what type it is.

Makoto
  • 104,088
  • 27
  • 192
  • 230
  • I think it is funny, all these years later I am still getting downvoted on my question when the "obvious" answer in the comments above is wrong, and your _slightly_ less-obvious answer is the correct one. Thanks again for the answer – roberto tomás Sep 05 '19 at 15:14
0

static fields are always looked up by static type, so it doesn't matter what you assign to n; n.TYPE will always be WordCategories.Noun.TYPE. Heck, n could even be null:

System.out.println(((WordCategories.Noun) null).TYPE); // Doesn't cause an exception.

There is no way to get n.TYPE to behave the way you want with a static TYPE field. You will need to either make it non-static (and make all your objects bigger in the process), or you will need to change the way you access this data.

Instead of trying to access this through a static field, I recommend using an instance method:

public class WordCategories {
  public static abstract class Noun {
    public static final String TYPE = null;
    public abstract String getType();
    //... and so on, methods
  }
  public static class Person extends Noun {
    public static final String TYPE = "Person";
    public String getType() {
      return TYPE;
    }
    // ...
  }
}

...

for(WordCategories.Noun n: nouns) {
  if(n.getType().equals("Person") ){
    ...
  }
}
user2357112
  • 260,549
  • 28
  • 431
  • 505
  • Two downvotes? Are those "you got something wrong" downvotes, "you should've left the question unanswered" downvotes, or what? – user2357112 Apr 11 '16 at 03:22
  • Well — you didn't answer my question, you just found away around it. Temporarily revoking the answer credit, in case someone felt like they could have done better but were beaten to the punch by a cheap answer. – roberto tomás Apr 11 '16 at 03:23
  • @robertotomás: I've made it more explicit that the answer is "no, that won't work". – user2357112 Apr 11 '16 at 03:29
  • I'm still not convinced about that — If I inject `Class c = n.getClass` in that part of the code and run it in the debugger, it returns `Person` not `Noun`. – roberto tomás Apr 11 '16 at 03:31
  • @robertotomás: Yes, because `getClass` isn't static. `n.TYPE` *does not care what `n` is*. `n` isn't involved at all. – user2357112 Apr 11 '16 at 03:33
  • 1
    @robertotomás, this IS the correct answer to your question... If you want the type of an object, you need to call an instance member (not a static one)... And since fields cannot be overridden, it has to be a method (`getType()`)... P.S: `getClass()` is an **instance method** too... – Codebender Apr 11 '16 at 03:34
  • I want to point out that at first I accepted your answer,.. and in all likely hood I will come back and mark it as the answer again. because you were downvoted I gave people a chance to answer with their own ideas: this was actually fruitful, we got other submissions. In any case, I appreciarte your answer. I used it in my project already. I was just bowing to the whim of stackoverflow here :) – roberto tomás Apr 11 '16 at 04:26
0

Try the test case:

public static void main(String[] args) {
    Base sub1 = new Sub();
    System.out.println(sub1.TYPE); // will print 'BASE'
    Sub sub2 = new Sub();
    System.out.println(sub2.TYPE); // will print 'SUB'
}
static class Base {
    static String TYPE = "BASE";
}
static class Sub extends Base {
    static String TYPE = "SUB";
}

If you access static field by the instance, it is decided by instance's declare Class, not the instance's real Class.

In your code for(WordCategories.Noun n: nouns) {, n's declare Class is Noun,so whatever n's real Class is,n.TYPE will only be null.

So, either use directly Class to access static fields, or use non-static field or method. Your usage is not wise, you should just take a different way.

Maybe you just need a non-static method:

public String getType() {
    return TYPE;
}
magooup
  • 134
  • 6
  • "So, either use directly Class to access static fields" that's sorta what I'd have iked to have done. except I want to use `n.getClass` to get there, maybe. – roberto tomás Apr 11 '16 at 05:48
  • @robertotomás You just need a non-static method: ```public String getType() { return TYPE; }``` – magooup Apr 11 '16 at 05:54
  • @robertotomás You must always avoid this kind of usage: using instance reference to access static field or method directly. – magooup Apr 11 '16 at 06:01