java - 如何定义读取所有InputStream(包括ZipInputStream)的方法?

标签 java inputstream liskov-substitution-principle zipinputstream

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

我认为 Java 的 ZipInputStream 违反了有关 InputStream 抽象类的里氏替换原则 (LSP)。如果 ZipInputStream 是 InputStream 的子类型,则程序中的 InputStream 类型的对象可以替换为 ZipInputStream 类型的对象,而不改变该程序的任何所需属性(正确性、执行的任务等)。

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

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

the total number of bytes read into the buffer, or -1 if there is no more data because the end of the stream has been reached.

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

the actual number of bytes read, or -1 if the end of the entry is reached

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

现在来看演示该问题的代码。 (这是我实际尝试做的事情的简化版本,因此请原谅任何糟糕的风格、多线程问题或流高级等事实。)。

接受任何输入流以生成流的 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 存在根本缺陷吗?

最佳答案

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

在任何时候,ZipInputStream实际上是条目的输入流,而不是整个 ZIP 文件。很难看出还有什么 ZipInputStream.read()除了返回 -1 之外,还可以在条目结束时执行此操作。

this will break the correctness property of any method that tries to use it in such a way

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

关于java - 如何定义读取所有InputStream(包括ZipInputStream)的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29484596/

相关文章:

java - 检查线程是否被中断?

java - 比较Java中的扑克手牌

Java serversocket(http) 从 POST 读取二进制数据

solid-principles - 什么是被拒绝的遗赠?

java - 模式验证错误的自定义映射器

java - 具有 JTable 多行列的自定义渲染器

java - 如何克隆 InputStream?

java - 如何从 Java 中的 InputStream 对象中读取包含换行符的字符串?

swift - 如何使用共享代码为更有限版本的类建模?

oop - 符合 Liskov 的状态设计模式