Java: переопределение статической переменной родительского класса?

У меня есть следующий класс, который я использую в качестве основы для всех моделей в моем проекте:

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..
}

Затем я пытаюсь перейти от него следующим образом:

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

Однако, если я попытаюсь сделать следующее:

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

Затем вместо запроса: "SELECT id FROM user WHERE id = ?" создается запрос: "SELECT id from null WHERE id =?". Таким образом, переопределение поля table в классе User не имеет никакого эффекта.

Как я могу это преодолеть? Если я добавил метод setTable() в BaseModel и вызвал setTable() в конструкторе User, будет ли новое значение table доступно для всех методов класса User?

Ответ 1

Вы не можете переопределять статические методы или поля любого типа в Java.

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

Это создает новое поле User#table, которое просто имеет то же имя, что и BaseModel#table. Большинство IDE предупредит вас об этом.

Если вы измените значение поля в BaseModel, оно применимо ко всем другим классам моделей.

Один из способов - использовать базовые методы 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();
}

и использовать его в подклассе

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

Если вы хотите использовать полевой подход, вам нужно создать класс BaseDAO и иметь UserDAO (по одному для каждого класса модели), который соответствующим образом устанавливает поле. Затем вы создаете одиночные экземпляры всех даос.

Ответ 2

Поскольку Java не позволяет вам переопределять членов static, вам в основном нужно прибегнуть к чуть более подробному, но в целом приятному singleton pattern, в котором вы по-прежнему концептуально пишете "статический" код, но вы технически используете (глобальные/одиночные/статические) экземпляры, поэтому вы не ограничены ограничениями static.

(обратите внимание, что вам также нужно использовать методы, потому что поля не участвуют в полиморфизме и, следовательно, не могут быть переопределены)

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);
}

Выходы:

SELECT count(*) FROM user WHERE id = 123
SELECT count(*) FROM post WHERE id = 456

Ответ 3

Чтобы сделать то, что вы хотите сделать, не ставьте table static в BaseModel. Затем в других классах, которые наследуют от BaseModel, вы можете установить table в конструкторе по умолчанию на все, что пожелаете.

static {
    table = "user";
}