Как определить метод для чтения всех InputStreams, включая ZipInputStream?

Я спрашивал об этом однажды, и мой пост был удален за то, что он не предоставил код, использующий вспомогательный класс. На этот раз я создал полный набор тестов, который показывает точную проблему.

Я считаю, что ZipInputStream в Java нарушает принцип подстановки Лискова (LSP) в отношении абстрактного класса InputStream. Чтобы 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 в корне ошибочен?


person steinybot    schedule 07.04.2015    source источник


Ответы (1)


Я не вижу нарушения LSP. Документация для ZipInputStream.read(byte[], int, int) говорит: "Читает из текущей записи ZIP в массив байтов".

В любой момент времени ZipInputStream на самом деле является входным потоком записи, а не всего ZIP-файла. И трудно понять, что еще ZipInputStream.read() может сделать в конце записи, кроме возврата -1.

это нарушит свойство правильности любого метода, который попытается использовать его таким образом.

Трудно понять, как метод когда-либо узнает.

person user207421    schedule 07.04.2015
comment
Это на самом деле не отвечает на вопрос. Я спрашивал, можно ли написать такой метод, в котором он может принимать InputStream и генерировать SHA1 из всего потока независимо от фактической реализации InputStream. - person steinybot; 07.04.2015
comment
@Steiny И я ответил на него. Если вы не хотите, чтобы ZIPInputStream распаковывал записи, не используйте его. Но это то, что он делает, если вы его используете. Кажется, вы ожидаете, что он доставит весь поток. Это не так. - person user207421; 25.08.2015