【问题标题】:Java: Overriding static variable of parent class?Java:覆盖父类的静态变量?
【发布时间】:2013-10-27 17:41:29
【问题描述】:

我使用以下类作为项目中所有模型的基础:

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 = ?”。因此,覆盖User 类中的table 字段似乎没有任何效果。

我该如何克服这个问题?如果我在 BaseModel 中添加了一个setTable() 方法,并在User 的构造函数中调用了setTable(),那么table 的新值是否也可用于User 类的所有方法?

【问题讨论】:

    标签: java oop static static-members


    【解决方案1】:

    您不能在 Java 中覆盖任何类型的静态方法或字段。

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

    这会创建一个新字段User#table,它恰好与BaseModel#table 同名。大多数 IDE 都会对此发出警告。

    如果您更改 BaseModel 中字段的值,它也将应用于所有其他模型类。

    一种方法是让基本方法通用

    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(每个模型类一个)来相应地设置字段。然后创建所有 daos 的单例实例。

    【讨论】:

    • 为什么 IDE 会警告数据隐藏?
    • @Click:我扩展了我的答案。
    • exists() 只是一个例子,我还有另外 10-20 个这样的辅助方法。我不想重载所有这些,我宁愿只更改表名属性,以便它们都能正常工作。
    • @Arian 这完全是意料之中的行为。只是OP没有预料到这一点。 OP 正在谈论覆盖字段,这从未完成。甚至没有实例字段。
    • “OP 没想到”所以这是出乎意料的 ;) ...另外,谁对我的回答投了反对票?至少给个理由吧,胆小鬼!
    【解决方案2】:

    因为 Java 不允许您覆盖 static 成员,所以您基本上需要求助于稍微冗长但总体上更好的 singleton pattern,其中您仍然在概念上编写“静态”代码,但您是技术上使用 (global/singleton/"static") 实例,因此您不受 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
    

    【讨论】:

    • 这是在 Scala 中添加了一些幼稚的反射后的样子,没有单例样板:pastebin.com/3iujwygZ
    【解决方案3】:

    为了做你想做的事,不要在BaseModel 中将table 设为静态。然后在继承自BaseModel 的其他类中,您可以在默认构造函数中将table 设置为您想要的任何值。

    static {
        table = "user";
    }
    

    【讨论】:

    • 那我不能将User.exists()作为静态方法调用。
    • @ClickUpvote 这可能表明您误用了 static 关键字。我建议您查看您的设计并对其进行重新设计,以摆脱对 static exists() 方法的需求
    • @StormeHawke 我的设计有什么问题?如果我创建一个用户模型实例只是为了检查给定的用户 ID 是否有效,会更清楚吗?
    • @ClickUpvote 不是说你的设计有有问题,只是说可能是你在跑步时需要看的东西像这样的东西,Java 语言的设计不允许你做你想做的事
    • @ClickUpvote 如果您也将用户的默认构造函数设为静态,则可以。我没有在我的示例中包含它(我感到羞耻),因为我认为你会看到这是它与你当前的实现一起工作的唯一方法。
    猜你喜欢
    • 1970-01-01
    • 2014-07-22
    • 1970-01-01
    • 2010-10-10
    • 1970-01-01
    • 2016-03-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多