泛型,界面谜题,这样声明界面有什么好处?

时间:2013-08-08 11:38:11

标签: java generics interface

在我的项目中,我看到这样的界面。所有模型都扩展了界面。我想知道有什么用?

public interface IModel {

     <T> T modelTo(Class<T> clazz);
}

public interface IPerson extends IModel {

    public String getFirstName();

    public void setFirstName(String firstName);

    public String getMiddleName();

    public void setMiddleName(String middleName);

}

然后在代码中的某些地方我看到了

@Override
    public void modelJoin(IModel parent, IModel sample) {
      //Some code
      IPerson sample= sample.modelTo(IPerson.class);
      IPerson person = parent.modelTo(IPerson.class);

      //Some code

   }

你能解释一下它的见解吗?

3 个答案:

答案 0 :(得分:2)

看起来像是使用Adapter pattern。我们的想法是创建一个给定另一个类的类的“视图”,或者调整一种类作为另一个类。

一个简单的现实世界的例子可以是电插座。在不同的国家,使用不同类型的插座。因此,您使用适配器将手机插入通常无法“识别”的电源插座中。

当然,这可以使用面向对象编程和适配器模式进行建模。使用您的IModel接口,但命名为IAdaptable,它可以像这样使用。

public interface IAdaptable {
    <T> T adaptAs(Class<T> clazz);
}
public interface IChargeAmerican { void chargePhoneInAmerica(); }
public interface IChargeEurope { void chargePhoneInEurope(); }

public class EuropeanSocket implements IAdaptable, IChargeEurope {
    public <T> T adaptAs(Class<T> clazz) {
        if (clazz.equals(IChargeAmerican.class)) {
            return new EuropeanSocketToAmericanSocketAdapter(this);
        }
        throw new RuntimeException("unknown");
    }

    public void chargePhoneInEurope() {
        ;
    }
}

public class AmericanSocket implements IChargeAmerican {
    public void chargePhoneInAmerica() {
         ;
    }
}

public class EuropeanSocketToAmericanSocketAdapter implements IChargeAmerican {
    private EuropeanSocket socket;
    public EuropeanSocketToAmericanSocketAdapter(EuropeanSocket socket) {
         this.socket = socket;
    }

    public void chargePhoneInAmerica() {
         socket.chargePhoneInEurope();
    }
}

使用它可以简单地将欧洲插座改为美国插座,有点像在两者之间插入适配器。

public void foo() {
    EuropeanSocket europe = new EuropeanSocket();
    IChargeAmerican murica = europe.adaptAs(IChargeAmerican.class);
    murica.chargePhoneInAmerica();
}

此示例显示adaptAs方法如何在两个接口IChargeAmerican和IChargeEurope之间创建链接。即使他们没有任何共同点,适配器也可以像他们一样行事。

现在,EuropeanSocket实现了IAdaptable接口,以便将自身“转换”为另一个已知套接字。通常虽然班级不应对此负责。正如维基百科上的例子所示,工厂或提供商更适合这一点。

答案 1 :(得分:1)

我认为你问为什么方法签名

<T> T modelTo(Class<T> clazz);

被使用。

参数clazz用于在实现的方法中包含类型信息。然后,您可以非常轻松地访问类型信息。

然后,您可以创建一个对象,并从具有给定类的已实现方法返回它。

方法签名看起来有点笨拙但有用,因为编译后会丢失通用信息(类型擦除),参数使您可以访问类型信息(因此也可以访问预期的返回类型)。

答案 2 :(得分:0)

我可以想象,通过将Class对象作为调用modelTo(Class clazz)方法的其他方法的参数传递,或者换句话说:使用其他方法将IModel对象转换为任何方法,可以通过这种方式允许类型转换class甚至不知道他们将把它投入哪个类(甚至没有阻止将Class实例传递给这个甚至不是IModel的子类型的方法......)

知道如何实现这个modelTo方法会很有趣。抽象骨架类中是否有单个最终实现?它如何响应错误(例如将null作为clazz参数传递,或触发ClassCastException)?换句话说:这可能是尝试将所有类转换封装到单个方法中,用自定义异常或类似的东西替换ClassCastExceptions吗? (ClassCastException是一个RuntimeException,它可能是一种确保抛出已检查异常的方法,而不是在代码中的每个地方强制执行显式异常处理,我已经看到使用这种方法的项目...)