Java: Overriding static variable of parent class?
Asked Answered
M

4

32

I have the following class which I'm using as the base of all the models in my project:

public abstract class BaseModel
{
    static String table;
    static String idField = "id";       

    public static boolean exists(long id) throws Exception
    {
        Db db = Util.getDb();
        Query q = db.query();
        q.select( idField ).whereLong(idField, id).limit(1).get(table);

        return q.hasResults();
    }

    //snip..
}

I'm then trying to extend from it, in the following way:

public class User extends BaseModel
{
    static String table = "user";
    //snip
}

However, if I try to do the following:

if ( User.exists( 4 ) )
   //do something

Then, rather than the query: "SELECT id FROM user WHERE id = ?", it is producing the query: "SELECT id from null WHERE id = ?". So, the overriding of the table field in the User class doesn't seem to be having any effect.

How do I overcome this? If I added a setTable() method to BaseModel, and called setTable() in the constructor of User, then will the new value of table be available to all methods of the User class as well?

Monochord answered 18/10, 2013 at 18:56 Comment(0)
B
27

You cannot override static methods or fields of any type in Java.

public class User extends BaseModel
{
    static String table = "user";
    //snip
}

This creates a new field User#table that just happens to have the same name as BaseModel#table. Most IDEs will warn you about that.

If you change the value of the field in BaseModel, it will apply to all other model classes as well.

One way is to have the base methods generic

protected static boolean exists(String table, long id) throws Exception
{
    Db db = Util.getDb();
    Query q = db.query();
    q.select( idField ).whereLong(idField, id).limit(1).get(table);

    return q.hasResults();
}

and use it in the subclass

public static boolean exists(long id)
{
    return exists("user", id);
}

If you want to use the field approach, you have to create a BaseDAO class and have a UserDAO (one for each model class) that sets the field accordingly. Then you create singleton instances of all the daos.

Branen answered 18/10, 2013 at 18:58 Comment(9)
Why would an IDE warn about data hiding?Beatrice
@Click: I extended my answer.Branen
exists() is only one example ,I have another 10-20 such helper methods. I don't want to overload all of them, I'd rather just change the table name property so they will all work.Monochord
@Rohit: because it causes unexpected behavior, as in OP's case.Branen
@Arian It's totally an expected behaviour. It's just that OP didn't expected that. OP is talking about overriding fields, which is never done. Not even for instance field.Beatrice
#685800Asberry
"OP didn't expected that" so it was unexpected ;) ... also, who downvoted my answer? At least give some reason, coward!Branen
#1448592Asberry
Is there any way to check, from within BaseModel.exists(), what the instance type of the class is? E.g using getClass().getName() or instance of? So if I could do if .. instance of User table = "user" .Monochord
H
12

Because Java doesn't allow you to override static members, you basically need to resort to the slightly more verbose but overall nicer singleton pattern, wherein you're still conceptually writing "static" code, but you're technically using (global/singleton/"static") instances, so you're not restricted by the limitations of static.

(note that you also need to use methods because fields don't participate in polymorphism, and thus cannot be overridden)

public abstract class BaseTable {
    public abstract String table();
    public String idField() { return "id"; }

    public boolean exists(long id) {
        // don't build queries this way in real life though!
        System.out.println("SELECT count(*) FROM " + table() + " WHERE " + idField() + " = " + id);
        return true;
    }
}

public class UserTable extends BaseTable {
    public static final User INSTANCE = new UserTable();
    private UseTabler() {}

    @Override public String table() { return "user"; }
}

public class PostTable extends BaseTable {
    public static final Post INSTANCE = new PostTable();
    private PostTable() {}

    @Override public String table() { return "post"; }
}

public static void main(String[] args) {
    UserTable.INSTANCE.exists(123);
    PostTable.INSTANCE.exists(456);
}

Outputs:

SELECT count(*) FROM user WHERE id = 123
SELECT count(*) FROM post WHERE id = 456
Henninger answered 5/4, 2014 at 14:52 Comment(1)
here's how it would look like in Scala with some naive reflection added, without the singleton boilerplate: pastebin.com/3iujwygZHenninger
H
5

In order to do what you are looking to do, don't make table static in the BaseModel. Then in the other classes that inherit from BaseModel, you can set table in the default constructor to whatever you wish.

static {
    table = "user";
}
Hillari answered 18/10, 2013 at 19:4 Comment(7)
Then I can't call User.exists() as a static method.Monochord
@ClickUpvote this could be an indication that you're misusing the static keyword. I'd recommend taking a look at your design and reworking it to get rid of the need for a static exists() methodWorld
@World What's wrong with my design? Will it be clearer if I made an instance of my user model just to check if the given user id is valid?Monochord
@ClickUpvote Not saying there is something wrong with your design, only that there could be something you need to look at if you're running into something like this where the design of the Java language doesn't allow you to do what you're wanting to doWorld
@ClickUpvote you could if you made the User's default constructor static as well. I didn't include that in my sample (shame on me) because I thought you would see that is the only way it would work with your current implementation.Hillari
@BrianDishaw If I made that static.. then could I also instantiate multiple new User objects with their own properties? Or will I be able to have only one User object at a time?Monochord
@ClickUpvote If you made a constructor static, then you would be able to interact with your other static methods in your base class. Nothing would prevent you from instantiating other User objects or having additional constructors that were non static with additional properties.Hillari
C
-1

I'm a 3 years seasoned Python developer and I am learning Java at the moment. I am solving this as follows but I believe it might have better solutions.

Main.java

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Person burakhan= new Person("Burakhan", 28);
        Person fatma = new Person("Fatma", 25);
        EnglishPerson david = new EnglishPerson("David", 22);
        GermanPerson bruno = new GermanPerson("Bruno", 23);

        Group group = new Group();

        group.addToGroup(burakhan);
        group.addToGroup(fatma);
        group.addToGroup(david);
        group.addToGroup(bruno);

        group.members();


    }
public class Person {
    String name;
    int age;

    static String country = "TR"; // default

    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    public void getOlder(){
        this.age++;
    }


    public void speak() throws NoSuchFieldException, IllegalAccessException {
        String sentence = "My name is %s and I am %d years old. I am from %s".formatted(this.name, this.age, getClass().getDeclaredField("country").get(this));
        System.out.println(sentence);
    }

}

EnglishPerson.java

package person;

public class EnglishPerson extends Person {
    static String country = "UK";

    public EnglishPerson(String name, int age) {
        super(name, age);
    }

}

GermanPerson.java

package person;

public class GermanPerson extends Person {
    static String country = "GER";

    public GermanPerson(String name, int age) {
        super(name, age);
    }
}

When I run the main, it looks like this.

My name is Burakhan and I am 28 years old. I am from TR
My name is Fatma and I am 25 years old. I am from TR
My name is David and I am 22 years old. I am from UK
My name is Bruno and I am 23 years old. I am from GER

Again, I am also learning Java and I believe this approach is a bit Pythonic. Love to hear other opinions

Chaste answered 27/1 at 9:46 Comment(1)
What you are doing, is not overriding, because it is not possible. Using reflection in this case is a bad idea in my opinionSchulz

© 2022 - 2024 — McMap. All rights reserved.