ほとんどの場合、この出版物は既に知識を持っている人によって読まれますが、念のために:IModelからの主なメソッドは次のとおりです。
T getObject();
抽象化は単純で簡潔ですが、実際にはそれほど単純ではありません。 カットの下-Java 8がどのように標準的なアプローチの冗長性と不安を打ち破ったのかを物語る。
データ
テストデータクラス:
public class User implements Serializable { private final String name; private final int age; public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } }
試み1:私たちは書いた、私たちは書いた...
タスクのソリューションにアプローチする方法は次のとおりです。
public class AbstractReadOnlyModelPanel extends Panel { public AbstractReadOnlyModelPanel(String id, IModel<User> model) { super(id, model); add(new Label("name", new AbstractReadOnlyModel<String>() { @Override public String getObject() { return model.getObject().getName(); } })); add(new Label("age", new AbstractReadOnlyModel<Integer>() { @Override public Integer getObject() { return model.getObject().getAge(); } })); } }
すべてが安価で、信頼性が高く、実用的です。AbstractReadOnlyModelの匿名サブクラスを作成します。 すぐに動作し、明らかに見えます。 1つの問題は、同じ匿名クラスを使用するとコードが面倒になることです。コンポーネントあたり6行は冗談ではありません。
試行2:脚の方向に撃つ
今PropertyModelを試してください:
public class PropertyModelPanel extends Panel { public PropertyModelPanel(String id, IModel<User> model) { super(id, model); add(new Label("name", PropertyModel.of(model, "name"))); add(new Label("age", PropertyModel.of(model, "age"))); } }
うわー、はるかにコンパクト。 しかし、蜂蜜の樽には多くのタールがあります:
- 何よりもまず、コンパイラは私たちのアシスタントではありません。 存在しないプロパティを指定したり、間違ったタイプのプロパティを指定したり、さらに多くの興味深いエラーを作成したりできます。
- 第二に、反射。 重要ではありませんが、その存在は幸せではありません。
ラムダがステージに登場
幸いなことに、Java 8は長い間リリースされており、 ラムダとメソッドリファレンスは急いで助けになりました。
public class GetterModel<E, P> extends AbstractReadOnlyModel<P> { private final E entity; private final IModel<E> entityModel; private final IPropertyGetter<E, P> getter; private GetterModel(E entity, IModel<E> entityModel, IPropertyGetter<E, P> getter) { this.entity = entity; this.entityModel = entityModel; this.getter = getter; } public static <E, P> GetterModel<E, P> ofObject(E entity, IPropertyGetter<E, P> getter) { Objects.requireNonNull(entity, "Entity cannot be null"); Objects.requireNonNull(getter, "Getter cannot be null"); return new GetterModel<>(entity, null, getter); } public static <E, P> GetterModel<E, P> ofModel(IModel<E> entityModel, IPropertyGetter<E, P> getter) { Objects.requireNonNull(entityModel, "Entity model cannot be null"); Objects.requireNonNull(getter, "Getter cannot be null"); return new GetterModel<>(null, entityModel, getter); } @Override public P getObject() { return getter.getPropertyValue(getEntity()); } private E getEntity() { return entityModel != null ? entityModel.getObject() : entity; } }
public interface IPropertyGetter<E, P> { P getPropertyValue(E entity); }
さて、すぐにこのモデルの実装で書き直された例:
public class GetterModelPanel extends Panel { public GetterModelPanel(String id, IModel<User> model) { super(id, model); add(new Label("name", GetterModel.ofModel(model, User::getName))); add(new Label("age", GetterModel.ofModel(model, User::getAge))); } }
さらに、PropertyModelの例とほぼ同じくらい簡潔に:
- タイプセーフ:コンパイラは、タイプが一致することを検証します(もちろん、Labelよりも読みやすいコンポーネントを使用しない限り)。
- タイプミスに関してははるかに安全です。間違えた場合、コンパイラがそれをキャッチする可能性があります。
- 反射は使用されません。
ただし、PropertyModelと比較すると、いくつかの欠点があります。
- GetterModelは読み取り専用ですが、PropertyModelでは書き込みが可能です。 セッターを追加すると、優雅さの概念が失われ、エラーの別の原因も追加されます(1つのプロパティからセッターを指定し、別のプロパティからゲッターを指定することが可能になります)。
- PropertyModelでは、「outerObject.itsProperty.propertyOfProperty」という形式の式を使用して、ネストされたプロパティにアクセスできます。
しかし、素晴らしいボーナスがあります:データソースおよびモデルとして使用されるPropertyModelの魔法の能力の類似体であり、POJOは魔法なしで実装されます:2つのファクトリメソッド(ofModel()およびofObject())を追加しました。