【问题标题】:How to define a method for reading all InputStreams including ZipInputStream?如何定义读取所有 InputStreams 包括 ZipInputStream 的方法?
【发布时间】:2015-06-11 15:17:27
【问题描述】:

我之前问过一次,我的帖子因为没有提供使用帮助类的代码而被删除。这次我创建了一个完整的测试套件来显示确切的问题。

我认为 Java 的 ZipInputStream 在 InputStream 抽象类方面打破了里氏替换原则 (LSP)。如果 ZipInputStream 是 InputStream 的子类型,那么程序中 InputStream 类型的对象可以被 ZipInputStream 类型的对象替换,而不会改变该程序的任何所需属性(正确性、执行的任务等)。

这里违反LSP的方式是针对read方法的。

InputStream.read(byte[], int, int) 声明它返回:

读入缓冲区的总字节数,如果由于已到达流的末尾而没有更多数据,则为 -1。

ZipInputStream 的问题在于它修改了 -1 返回值的含义。它指出:

实际读取的字节数,如果到达条目末尾,则为 -1

(实际上Android文档http://developer.android.com/reference/java/util/zip/ZipInputStream.html中的available方法有类似问题的提示)

现在是演示问题的代码。 (这是我实际尝试做的缩减版,请原谅任何糟糕的风格、多线程问题或流是高级的事实等)。

接受任何 InputStream 以生成流的 SHA1 的类:

public class StreamChecker {

    private byte[] lastHash = null;

    public boolean isDifferent(final InputStream inputStream) throws IOException {
        final byte[] hash = generateHash(inputStream);
        final byte[] temp = lastHash;
        lastHash = hash;
        return !Arrays.equals(temp, hash);
    }

    private byte[] generateHash(final InputStream inputStream) throws IOException {
        return DigestUtils.sha1(inputStream);
    }
}

单元测试:

public class StreamCheckerTest {

    @Test
    public void testByteArrayInputStreamIsSame() throws IOException {
        final StreamChecker checker = new StreamChecker();
        final byte[] bytes = "abcdef".getBytes();
        try (final ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) {
            Assert.assertTrue(checker.isDifferent(stream));
        }
        try (final ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) {
            Assert.assertFalse(checker.isDifferent(stream));
        }
        // Passes
    }

    @Test
    public void testByteArrayInputStreamWithDifferentDataIsDifferent() throws IOException {
        final StreamChecker checker = new StreamChecker();
        byte[] bytes = "abcdef".getBytes();
        try (final ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) {
            Assert.assertTrue(checker.isDifferent(stream));
        }
        bytes = "123456".getBytes();
        try (final ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) {
            Assert.assertTrue(checker.isDifferent(stream));
        }
        // Passes
    }

    @Test
    public void testZipInputStreamIsSame() throws IOException {
        final StreamChecker checker = new StreamChecker();
        final byte[] bytes = "abcdef".getBytes();
        try (final ZipInputStream stream = createZipStream("test", bytes)) {
            Assert.assertTrue(checker.isDifferent(stream));
        }
        try (final ZipInputStream stream = createZipStream("test", bytes)) {
            Assert.assertFalse(checker.isDifferent(stream));
        }
        // Passes
    }

    @Test
    public void testZipInputStreamWithDifferentEntryDataIsDifferent() throws IOException {
        final StreamChecker checker = new StreamChecker();
        byte[] bytes = "abcdef".getBytes();
        try (final ZipInputStream stream = createZipStream("test", bytes)) {
            Assert.assertTrue(checker.isDifferent(stream));
        }
        bytes = "123456".getBytes();
        try (final ZipInputStream stream = createZipStream("test", bytes)) {
            // Fails here
            Assert.assertTrue(checker.isDifferent(stream));
        }
    }

    private ZipInputStream createZipStream(final String entryName,
            final byte[] bytes) throws IOException {
        try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                final ZipOutputStream stream = new ZipOutputStream(outputStream)) {
            stream.putNextEntry(new ZipEntry(entryName));
            stream.write(bytes);
            return new ZipInputStream(new ByteArrayInputStream(
                    outputStream.toByteArray()));
        }
    }
}

回到问题... LSP 被违反,因为您可以读取 InputStream 的流末尾,但不能读取 ZipInputStream,当然这会破坏任何尝试使用它的方法的正确性属性这样的方式。

有什么方法可以实现,还是 ZipInputStream 存在根本缺陷?

【问题讨论】:

    标签: java inputstream liskov-substitution-principle zipinputstream


    【解决方案1】:

    我没有发现 LSP 违规。 ZipInputStream.read(byte[], int, int) 的文档说“从当前 ZIP 条目读取到字节数组中”。

    在任何时候,ZipInputStream 确实是条目的输入流, 而不是整个 ZIP 文件。很难看出ZipInputStream.read() 在输入结束时除了返回-1 还能做什么。

    这将破坏任何尝试以这种方式使用它的方法的正确性属性

    很难看出该方法是如何知道的。

    【讨论】:

    • 这并不能真正回答问题。我在问是否有可能编写这样的方法,它可以采用 InputStream 并从整个流中生成 SHA1,而不管 InputStream 的实际实现是什么。
    • @Steiny 我已经回答过了。如果您不希望 ZIPInputStream 解压缩条目,请不要使用它。但是,如果您确实使用它,那就是它的作用。您似乎期望它提供整个流。它没有。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-14
    • 1970-01-01
    • 2016-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    相关资源
    最近更新 更多