StringBuilder和CharBuffer都实现了CharSequence和Appendable接口。 声明超界面时
public interface IAppendableCharSequence extends CharSequence, Appendable{}
然后我可以将CharBuffer转换为IAppendableCharSequence,但不能转换为StringBuilder:
private IAppendableCharSequence m_buffer;
// ...
m_buffer = (IAppendableCharSequence) CharBuffer.allocate(512); // ok
m_buffer = (IAppendableCharSequence) new StringBuilder(512); // Cannot cast from StringBuilder to IAppendableCharSequence
为什么?谢谢!
答案 0 :(得分:4)
这些都不会在运行时实际工作,但是编译器允许一个而不是另一个的原因是StringBuilder
是final
而CharBuffer
不是<。 / p>
编译器确切知道instanceof StringBuilder
的任何内容都不能成为IAppendableCharSequence
的有效实现,因为StringBuilder
本身不实现该接口,并且final
它没有子类。因此,在任何情况下,该演员表都不合法,并且编译器拒绝它。
在CharBuffer
的情况下,编译器没有这种保证,因为您可能会创建{em> 实现CharBuffer
的自定义子类IAppendableCharSequence
}。
编译器允许强制转换的规则是Java语言规范,在这种情况下section 5.1.6(缩小引用转换),其中包括允许转换3 p>
从任何类类型
C
到任何非参数化的接口类型K
,前提是C
不是final
且未实现K
即。从任何类类型转换为类未实现的任何接口类型是有效的,前提是该类不是最终的。
答案 1 :(得分:2)
我可以将CharBuffer强制转换为IAppendableCharSequence
实际上你不能。您只能将类的实例强制转换为类故意实现的类型。 CharBuffer实现Appendable和CharSequence这一事实并不意味着它实现了IAppendableCharSequence接口。
编译器允许强制转换,因为它无法确定CharBuffer.allocate(512)
将返回的确切内容。就编译器所知,它可以返回一个CharBuffer的子类,它明确地实现了IAppendableCharSequence。但是如果对象没有真正实现该接口,则转换将在运行时抛出ClassCastException。
然而,new StringBuilder(512)
保证新对象是StringBuilder而不是它的子类,因此编译器可以在编译时看到演员不会工作。
您的问题的一个解决方案是制作一个实现您的界面的通用包装器:
public static <T extends CharSequence & Appendable> IAppendableCharSequence wrap(T t) {
if (t == null) throw new NullPointerException();
final CharSequence csq = t;
final Appendable a = t;
return new IAppendableCharSequence() {
@Override
public int length() {
return csq.length();
}
@Override
public char charAt(int index) {
return csq.charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return csq.subSequence(start, end);
}
@Override
public Appendable append(CharSequence s) throws IOException {
a.append(s);
return this;
}
@Override
public Appendable append(CharSequence s, int start, int end) throws IOException {
a.append(s, start, end);
return this;
}
@Override
public Appendable append(char c) throws IOException {
a.append(c);
return this;
}
};
}
(声明的csq
和a
变量在那里并不是必需的,因为可以直接在t
上调用相同的方法,但是额外的变量会使返回的IAppendableCharSequence对象变得有点通过避免每次调用其中一个方法时都需要进行转换更快。声明这些变量也会进行早期安全检查,调用者没有绕过泛型,否则只有在尝试使用时才会导致失败。返回IAppendableCharSequence。)
一旦你有了这个方法,你就可以做到这两点:
m_buffer = wrap(CharBuffer.allocate(512));
m_buffer = wrap(new StringBuilder(512));
您也可以使用其他任何实现CharSequence和Appendable的方法来调用它。
答案 2 :(得分:1)
@Ian Roberts是对的。
难题的另一部分是Java类型系统将IAppendableCharSequence
视为不只是CharSequence
和Appendable
。它实际上是一种类型,可以具有相关的语义......这样,&#34;任何旧类&#34; CharSequence
和Appendable
都不符合条件。
这意味着下级CharBuffer
或StringBuilder
是-a IAppendableCharSequence
,即使它们都实现了CharSequence
和{{1} }接口。
Java接口更多是C或C ++别名,即使它们只扩展其他接口......