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