1

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

这是为什么?谢谢!

4

3 回答 3

4

这些都不会在运行时实际工作,但编译器允许一个而不是另一个的原因是它StringBuilderfinalCharBuffer不是。

编译器确定没有任何东西instanceof StringBuilder可以成为有效的实现,IAppendableCharSequence因为StringBuilder它本身没有实现该接口,并且final它不能有子类。因此,在任何情况下该强制转换都是合法的,并且编译器会拒绝它。

CharBuffer编译器没有这种保证的情况下,因为您可能会创建一个确实实现的自定义子CharBuffer类。IAppendableCharSequence

编译器允许哪些类型转换的规则在 Java 语言规范中,在本例中为第 5.1.6 节(缩小引用转换),其中允许转换

从任何类类型C到任何非参数化接口类型K,前提C是没有final也没有实现K

即,从任何类类型到该类未实现的任何接口类型的强制转换都是有效的,前提是该类不是 final

于 2014-07-02T10:43:54.753 回答
2

我可以将 CharBuffer 转换为 IAppendableCharSequence

其实你不能。您只能将类的实例强制转换为该类有意实现的类型。CharBuffer 实现 Appendable 和 CharSequence 的事实并不意味着它实现了 IAppendableCharSequence 接口。

编译器允许强制转换,因为它无法判断CharBuffer.allocate(512). 据编译器所知,它可以返回一个明确实现 IAppendableCharSequence 的 CharBuffer 子类。但如果对象没有真正实现该接口,则强制转换将在运行时抛出 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;
        }
    };
}

(声明的csqa变量在此处并不是绝对必要的,因为可以t直接调用相同的方法,但是额外的变量通过避免每次调用其方法之一时都需要进行强制转换,从而使返回的 IAppendableCharSequence 对象更快一些。声明这些变量还会进行早期安全检查,即调用者没有绕过泛型,否则只有在尝试使用返回的 IAppendableCharSequence 时才会导致失败。)

一旦你有了这个方法,你就可以做到这两个:

m_buffer = wrap(CharBuffer.allocate(512));
m_buffer = wrap(new StringBuilder(512));

你也可以用其他实现 CharSequence 和 Appendable 的东西来调用它。

于 2014-07-02T10:51:40.977 回答
1

@Ian Roberts 是正确的。

谜题的另一部分是 Java 类型系统 IAppendableCharSequence不仅仅将. 它实际上是一种本身可能具有相关语义的类型......因此“任何旧类”既是 a又不合格。CharSequenceAppendableCharSequenceAppendable

这意味着 netherCharBufferStringBuilder is-a IAppendableCharSequence,即使它们都实现了CharSequenceAppendable接口。

Java 接口比 C 或 C++ 别名更多,即使它们只扩展其他接口......

于 2014-07-02T10:50:00.423 回答