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