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

我认为Java的ZipInputStream在InputStream抽象类方面打破了Liskov替代原理(LSP)。为了使ZipInputStream成为InputStream的子类型,可以将程序中的InputStream类型的对象替换为ZipInputStream类型的对象,而无需更改该程序的任何所需属性(正确性,执行的任务等)。

此处违反LSP的方式适用于读取方法。

InputStream.read(byte [],int,int)指出它返回:


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


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


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


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

现在查看演示问题的代码。 (这是我实际尝试做的精简版,请原谅任何不良的样式,多线程问题或流是高级的事实等)。

接受任何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根本存在缺陷?

最佳答案

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

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


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


很难看到该方法将如何知道。

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

10-10 09:05