1

私は Android アプリを構築しており、テーブルのやり取りを容易にするためにベース DBObject クラスをセットアップしました。DBObject コンストラクターはカーソルを設定し、それを仮想メソッド loadRecord に渡します。サブクラスはそれをオーバーライドして独自の属性をロードします。loadRecord の最後の Log.i 呼び出しによると、属性はすべて正しいですが、呼び出し元のメソッドがオブジェクトを取得すると、すべてのデータ メンバーには、ロードされた値ではなく、クラス宣言で定義されたデフォルトが含まれます (文字列は null です)。 、float は 0.00 など)。

public abstract class DBObject {
....

    public DBObject(DBHelper dbh, String table_name, int _id){
        // loads an existing DBObject from the database by id
        this.dbh = dbh;
        this.TABLE_NAME = table_name;
        this.id = _id;
        setupDbr();
        String sql = String.format("select * from %s where _id=%d", TABLE_NAME, id);
        Cursor cur = dbr.rawQuery(sql, null);
        if (cur.getCount() == 0){
            throw new RowDoesNotExistException(id);
        } else if (cur.getCount() > 1){
            throw new RowDoesNotExistException("Record is not unique.", id);
        }
        cur.moveToFirst();
        loadRecord(cur);
    }
....
abstract void loadRecord(Cursor cur);

このコンストラクターは、オーバーライドされた loadRecord を呼び出します。サブクラスでは次のようになります。

public class Expense extends DBObject{

    public String date = null;
    public String supplier = null;
    public String description = null;
    public float amount = 0.00f;
    public boolean receipt = false;

    public Expense(DBHelper dbh, String _id){
        super(dbh, DBHelper.EXPENSE_TABLE, _id);
...
    @Override
    void loadRecord(Cursor cur){
        id = cur.getInt(0);
        date = cur.getString(1);
        supplier = cur.getString(2);
        description = cur.getString(3);
        amount = cur.getFloat(4);
        receipt = (cur.getInt(5) == 1);
        Log.i("net.bradmont.reimburseit", String.format("%d, %s, %s, %s", id, date, supplier, description));
    }

DBObject コンストラクターで設定されたメンバーは、設定されたままになります。私はこれで完全に途方に暮れています....

前もって感謝します!

4

1 に答える 1

2

問題は、インスタンス変数の初期化子があることです。

public String date = null;
public String supplier = null;
public String description = null;
public float amount = 0.00f;
public boolean receipt = false;

初期化子は、スーパークラスコンストラクターの実行、サブクラスコンストラクターの本体が実行される前に実行されます。これは、コンストラクターでオーバーライドされたメソッドを呼び出す際の問題の1つです。これは、一般的に非常に悪い考えです。

この特定の問題は、インスタンス変数の初期化子を持たないことで修正できます。

public String date;
public String supplier;
public String description;
public float amount;
public boolean receipt;

これで、スーパークラスコンストラクターの完了とサブクラスコンストラクターの実行の間に余分な割り当てはありません。ただし、これは実際には優れたソリューションではありません。スーパークラスコンストラクターでオーバーライドされたメソッドを呼び出さないように再設計することをお勧めします。

また、パブリックフィールドも削除しようとします...クラスの実装の詳細が公開されないように、パブリックフィールドをプライベートにします。

コンストラクターの実行方法の詳細については、JLSのセクション12.5を参照してください。

于 2012-10-08T22:32:43.537 に答える