and*_*per 6 java extends interface class implements
这个问题适用于 Java 和 Android 开发人员。
在 Android 上,我希望有一个函数返回从View扩展的内容并实现Checkable,两者都是 Android API 的一部分。
在这里和互联网上的其他地方搜索了一下之后,我找到了这个解决方案:
private <T extends View & Checkable> T get() {
return (T) mView;
}
Run Code Online (Sandbox Code Playgroud)
请注意,mView 是扩展 View 的任何类型(并且有很多),但我保证它实现了 Checkable,因为只有这些类型才能存在。
这显示了“类型安全:未检查从视图到 T 的强制转换”的警告,但这不是问题。问题是我不能正确使用这个方法,例如:
View v=get(); //this works fine
Checkable c=get(); // this shows error
Run Code Online (Sandbox Code Playgroud)
第二行显示错误:
绑定不匹配:类型 ... 的泛型方法 get() 不适用于参数 ()。推断类型 Checkable&View 不是有界参数的有效替代品
所以我尝试使用 "," 而不是 "&" 。同样的错误。
但是,当我使用 "," 并颠倒顺序时,会收到警告“类型参数 View 正在隐藏类型 View”,并且这两行都可以正常工作。
这是代码:
private <T extends Checkable, View> T get() {
return null;
}
private void foo() {
final View v = get(); // this works fine
final Checkable c = get();// this works fine too
}
Run Code Online (Sandbox Code Playgroud)
但是,当我将返回的类型发送给其他函数时,它们不会将类型本身作为扩展类和接口的东西,并且需要一直转换(到视图)。
正确的做法是什么?
如果我展示的最后一种方式是正确的,为什么我会收到警告?如果总是最多扩展一个类,为什么顺序很重要?
在我深入研究泛型之前......你为什么不引入 aCheckableView并返回那个类型?
public class CheckableView extends View implements Checkable {
}
private CheckableView getCheckableView() {
return checkableView;
}
Run Code Online (Sandbox Code Playgroud)
如果View是一个接口,它可以工作:
interface View {}
interface Checkable {}
class CheckableView implements Checkable, View {}
public class Main {
private static CheckableView checkableView;
private static <T extends View & Checkable> T get() {
return (T) checkableView;
}
public static void main(String[] args) {
View view = Main.get();
Checkable checkable = Main.get();
}
}
Run Code Online (Sandbox Code Playgroud)
我认为理解泛型的问题在于,T extends View & Checkable在实际情况下并不意味着您可以返回任何扩展View和实现的类型Checkable。
这意味着客户端可以将返回的类型转换为任何扩展View和实现的类型Checkable。
客户可以做这样的事情:
class CheckableView extends View implements Checkable {}
class OtherView extends View implements Checkable {}
private static <T extends View & Checkable> T get() {
return (T) checkableView;
}
OtherView otherView = Main.get();
CheckableView checkableView = Main.get();
Run Code Online (Sandbox Code Playgroud)
但是您现在想对实施做什么?
private <T extends View & Checkable> T get() {
return ...;
}
Run Code Online (Sandbox Code Playgroud)
这个方法应该返回什么?如果您返回 a CheckableView,编译器会说您必须将其强制转换为T,因为客户端可能会将其强制转换为任何类型T。但这也意味着你会得到一个从 CheckableView 到 T的Unchecked cast警告,因为如果客户端将它投射到任何其他类型而不是CheckableView,例如OhterView,你会得到一个ClassCastException.
由于get方法实现者无法知道客户端会将其强制转换为哪种类型,因此他无法确定要返回哪个对象。
如果实现者返回特定对象(例如CheckableView),则客户端只能将其转换为该类型。那么为什么实现者不更改方法签名以返回该类型呢?.... 或者为这种情况引入特殊类型或接口?
也许这对你来说是一个解决方案
创建一个get方法将返回的接口,例如
public interface CheckableView {
}
Run Code Online (Sandbox Code Playgroud)
更改 get 方法以返回一个 CheckableView
private CheckableView get() {
}
Run Code Online (Sandbox Code Playgroud)
编写适应您的适配器Views表示实现Checkable的CheckableView
public class CheckableViewAdapter<T extends View & Checkable> implements CheckableView {
private T checkableView;
public CheckableViewAdapter(T checkableView) {
this.checkableView = checkableView;
}
}
Run Code Online (Sandbox Code Playgroud)
现在您可以CheckableView为每个View实现的 a 创建实例Checkable
class SomeView extends View implements Checkable {}
SomeView someView = ...;
CheckableView checkableView = new CheckableViewAdapter<SomeView>(someView);
Run Code Online (Sandbox Code Playgroud)
将方法的客户端get需要的方法添加到CheckableView接口中并在CheckableViewAdapter.
public interface CheckableView {
public void bringToFront();
}
public class CheckableViewAdapter<T extends View & Checkable> implements CheckableView {
private T checkableView;
public CheckableViewAdapter(T checkableView) {
this.checkableView = checkableView;
}
public void bringToFront(){
checkableView.bringToFront();
}
}
Run Code Online (Sandbox Code Playgroud)
要不就
public interface CheckableView {
public View getView();
public Checkable getCheckable();
}
Run Code Online (Sandbox Code Playgroud)
T您会收到错误,因为在第二个调用站点处为参数推断的类型(即 )Checkable c = get()很简单Checkable,正如错误消息正确报告的那样。
如果您强制调用使用兼容类型View & Checkable,它将编译。为此,您必须显式提供类型,例如:
private <T extends View & Checkable> T get() {
return (T) mView;
}
private <T extends View & Checkable> void useGeneric() {
View v = this.get(); // no need for an explicit type
Checkable c = this.<T>get(); // an explicit type needed
T t = this.get(); // best way
}
private void useSpecific() {
class Specific extends View implements Checkable {}
Checkable c = this.<Specific>get(); // risk of ClassCastException
Specific s = this.get(); // best way; risk of ClassCastException
}
Run Code Online (Sandbox Code Playgroud)
事实上,在分配给类的情况下,它不需要显式类型,而在接口情况下则需要显式类型,在我看来,这就像一种类型推断算法工件,一种类/接口不对称性。