将CharBuffer和StringBuilder转换为超接口

时间:2014-07-02 10:33:11

标签: java stringbuilder charsequence

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

为什么?谢谢!

3 个答案:

答案 0 :(得分:4)

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

编译器确切知道instanceof StringBuilder的任何内容都不能成为IAppendableCharSequence的有效实现,因为StringBuilder本身不实现该接口,并且final它没有子类。因此,在任何情况下,该演员表都不合法,并且编译器拒绝它。

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

编译器允许强制转换的规则是Java语言规范,在这种情况下section 5.1.6(缩小引用转换),其中包括允许转换

  

从任何类类型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;
        }
    };
}

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

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

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

您也可以使用其他任何实现CharSequence和Appendable的方法来调用它。

答案 2 :(得分:1)

@Ian Roberts是对的。

难题的另一部分是Java类型系统将IAppendableCharSequence视为不只是CharSequenceAppendable。它实际上是一种类型,可以具有相关的语义......这样,&#34;任何旧类&#34; CharSequenceAppendable都不符合条件。

这意味着下级CharBufferStringBuilder 是-a IAppendableCharSequence,即使它们都实现了CharSequence和{{1} }接口。

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