【问题标题】:Using default keyword in interface correctly在界面中正确使用默认关键字
【发布时间】:2020-03-11 12:21:01
【问题描述】:

我有一个同事需要一个方法可以用于两个类。

他决定创建一个由这些类实现的新接口。

接口只有一个方法 default doThis(String parameter)

它没有任何其他接口方法,没有迹象表明该接口会添加其他方法。

我觉得这是对界面的错误使用,应该以不同的方式完成。即,也许一个类具有允许其他类通过使用该对象来使用它的方法。

有这方面经验的人有什么意见可以分享吗?

我可以根据您的 cmets 进行更详细的更新。

更新:

这是代码,问题仍然存在: 这是对默认方法的有效使用,还是应该以另一种方式完成此通用逻辑,例如将保存到首选项的 Utilities 类?

界面:

public interface LogInCookie {

    default void mapCookiesToPreferences(String cookie) {
        if (cookie.contains(MiscConstants.HEADER_KEY_REFRESH)) {
            String refreshToken = cookie.replace(MiscConstants.HEADER_KEY_REFRESH, StringUtils.EMPTY);
            SharedPrefUtils.addPreference(SharedPrefConstants.REFRESH_TOKEN, refreshToken);
        } 
    }
}
public class HDAccountActivity extends AbstractActivity implements LogInCookie {

    private void mapCookies(List<String> mValue) {
       LogInCookie.super.mapCookiesToPreferences(mValue); //ekh!
    }

}
public class BaseSplashPage extends AppCompatActivity implements DialogClickedCallBack, LogInCookie {

//method which uses this
private void mapCookiesToPreferences(List<String> headers) {
        int firstItemInHeader = 0;
        for (String header : headers) {
            String  mValue =  header.substring(firstItemInHeader,header.indexOf(MiscConstants.SEMICOLON));
            LogInCookie.super.mapCookiesToPreferences(mValue);  //ekh!
        }
    }

}

【问题讨论】:

  • Java 允许这样做。但我认为,实现这些常用方法的最佳且清晰的方法是 1)创建一个新的抽象类并扩展它 2)使用静态方法创建一个最终类。我个人不建议为此使用界面。
  • @ahsan.dev 在这种情况下,使用接口是一种奇怪的方式。
  • @ahsan.dev 很难得到一个关于所涉及的实体和目的细节很少的问题的明确答案。
  • 该方法看起来很容易作为辅助函数放在单独的类中。
  • @ahsan.dev 我认为霍尔格的回答是合理的。我会说这个界面是完全没有必要的,因此它是令人困惑的。

标签: java java-8 interface default


【解决方案1】:

接口中的default 方法没有定义其他方法,不能对实现类的实例做很多有用的事情。它只能使用继承自java.lang.Object的方法,这些方法不太可能携带与接口相关的语义。

如果代码根本不使用this 上的实例方法,换句话说,完全独立于this 实例,则应将其设为static,将包含类更改为不可实例化class 类型,即

final class SomeUtilClass {
    static void doThis(String parameter) {
    // ...
    }

    private SomeUtilClass() {} //no instances
}

并在使用此方法的类中使用import static packageof.SomeUtilClass.doThis;

这样,所有这些类都可以在没有限定类型名称的情况下调用doThis(…) 之类的方法,而无需误导性的类型层次结构。

当方法实际使用this 实例时,如前所述,这只能是从java.lang.Object 继承的方法,类型继承可能是合理的。由于这不太可能,您可能仍然认为类型层次结构具有误导性,并将代码重写为

final class SomeUtilClass {
    static void doThis(Object firstParameter, String parameter) {
    // ...
    }

    private SomeUtilClass() {} //no instances
}

使用firstParameter 代替this,可以像doThis(this, …) 一样调用。

【讨论】:

  • 我们不需要使用 PowerMock 来测试静态方法吗?由于这个原因,我认为静态方法很糟糕,应该尽可能避免测试?
  • 当你想测试一个方法时,你应该用准备好的输入调用它,并验证它是否产生了预期的输出。这就是单元测试的工作原理。大多数时候,使用了一个模拟工具,它的使用方式是错误的。您很少需要模拟工具进行测试。在这方面,default 方法和static 方法之间没有根本区别。您的同事使用它的方式,在任何一种情况下,您都无法在不模拟的情况下拦截调用(但如上所述,您通常很少需要它进行单元测试)。
  • @Holger 有一个隐藏的依赖项。您可能可以通过以下方式看到它:SharedPrefUtils。这有助于澄清这一点吗?
  • 不,我不明白。假设我有void foo() { var x = new ArrayList&lt;&gt;(); },那么我对ArrayList 有一个“隐藏依赖”,从foo 的签名中无法识别。现在,我改用void foo() { var x = Collections.singletonList(42); }。在这种情况下,我对Collections 类有一个“隐藏依赖”,我调用了它的static 方法。那么调用static 方法与任何其他“X 内部使用Y”场景有何不同?
【解决方案2】:

理想情况下,您应该将该方法 doThis() 放在两个类都扩展的抽象类中。但是,如果您需要实现多重继承,那么在这里使用接口就可以了。

具有静态方法doThis() 的类也可以静态调用。

这完全取决于您如何组织项目。

【讨论】:

  • “理想情况下,您应该将该方法 doThis() 放在两个类都扩展的抽象类中。” ...我不能同意这个解决方案是“理想的” ”。使用接口在 Java 中定义类型通常是更好的策略有几个原因。
  • @joe Caruso:我使用静态建议,我认为我们需要 PowerMock 才能测试该方法。由于这个原因,我一直认为使用静态方法是不好的。您对此有何评论?
【解决方案3】:

在 java 8 中,在接口中引入了 default 关键字,用于那些如果任何一组 api 具有较长的继承层次结构并且我们希望引入一个应该在所有较低级别的类中都可用的方法的情况。

所以对于前。在 Java 8 中,stream() 方法作为默认方法在 Collection 接口中被引入,并且最终在所有底层类中都可用。

就您所考虑的情况而言,如果我按照您的话进行,那么如果您是新开发的,那么您应该使用接口->抽象类->实际实现类。

只有当您的开发设置较旧并且您已经有从接口实现的类时,这可能是在您的接口中使用默认方法的理想方案。

【讨论】:

  • “接口中的默认关键字是为那些如果任何一组 api 具有长继承层次结构的情况引入的” ...不是真的。 Java 8 中添加了默认方法以支持新的语言扩展(尤其是 lambda 和流)。如果没有这个工具,现有的几个接口就会损坏。
  • @rootExplorr 我个人同意这个答案,但我不确定它的最终真相。
  • @scottb 完全正确。为了使 stream() 方法可用于一长串集合层次结构,引入了默认方法。这就是我的意思。
【解决方案4】:

接口中的默认方法
*) 可以有一个默认实现
*) 可以被实现类覆盖

是的,它是自 JAVA8 以来的正确用法。
接口中可以有默认方法,也可以有抽象方法

【讨论】:

  • 虽然您“可以”这样做,但它的用法是否正确?当我们只处理字符串时,我们也可以使用字符序列而不是字符串。但是,如果不需要,使用 CharSequence 不是正确的用法吗?例如: ``` private void doThat(CharSequence sequence) { sequence = "一大堆字符"; } ``` 除非有使用它的理由,否则我不确定我们为什么要使用它。就我而言,我不确定是否有理由在界面中使用 default 关键字。这似乎是解决现有问题的新方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-22
  • 1970-01-01
  • 2013-02-03
  • 2016-09-09
相关资源
最近更新 更多