Могу ли я изменить константу от int до байта в Java без нарушения обратной совместимости? - PullRequest
0 голосов
/ 08 января 2019

Я отправляю изменение на JNA , которое в предыдущих выпусках определяло набор констант как тип int, а именно:

int VER_EQUAL = 1;
int VER_GREATER = 2;
int VER_GREATER_EQUAL = 3;
... etc...

(Поскольку они определены в interface, они автоматически static и final.)

Эти константы используются в качестве аргумента Condition в функции VerSetConditionMask , для которой требуется аргумент BYTE, сопоставленный в JNA с byte.

в Java.

Чтобы использовать существующие константы int в этой функции, мне (и другим пользователям) пришлось бы явно типизировать их, поэтому я хочу изменить значения этих констант в библиотеке на byte, например,

byte VER_EQUAL = 1;
byte VER_GREATER = 2;
byte VER_GREATER_EQUAL = 3;
... etc...

Я не думаю, что это изменение нарушает обратную совместимость, потому что любой код, использующий эти константы, должен в настоящее время ожидать по крайней мере int и с радостью расширит byte без жалоб. Я написал много тестового кода, чтобы убедить себя в этом. Я знаю, что изменение типов Object (например, Integer на Byte) не будет работать, но я считаю, что это хорошо для примитивов. Тем не менее, несмотря на целый час поиска в Интернете подтверждения, я остаюсь немного неубедительным.

Это изменение обратно совместимо? Или я ошибаюсь, и может ли это привести к поломке существующего кода, основанного на примитиве int?

Ответы [ 2 ]

0 голосов
/ 08 января 2019

Может быть, это очевидно. Но, возможно, нет. Если какой-либо сторонний пользователь извлек значения int и сохранил их локально в Integer объектах, изменение значения int на byte приведет к ошибке времени компиляции:

interface IntToByteWithInt
{
    int VER_EQUAL = 1;
    int VER_GREATER = 2;
    int VER_GREATER_EQUAL = 3;
}

interface IntToByteWithByte
{
    byte VER_EQUAL = 1;
    byte VER_GREATER = 2;
    byte VER_GREATER_EQUAL = 3;
}

public class IntToByte
{
    public static void main(String[] args)
    {
        Integer a = IntToByteWithInt.VER_EQUAL;

        // Type mismatch: cannot convert from byte to Integer  
        Integer b = IntToByteWithByte.VER_EQUAL;
    }
}

Кроме того, я думаю, что по крайней мере следует упомянуть отражение для полноты.

Я также рассматривал троичный оператор (https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25.2) как горячий кандидат, но не смог вызвать ошибку с этим.

0 голосов
/ 08 января 2019

Это изменение обратно совместимо?

номер

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

// defined in `MyClass`
static final byte x = 10;

public static void main(String[] args) throws Throwable {
    lookup().findStaticGetter(MyClass.class, "x", int.class); // old code
}

Это выдает NoSuchFieldException, так как поиск ищет старый тип поля.

Это также относится к API написания байт-кода, которые обращаются к полю, например, с ASM :

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(55, ACC_PUBLIC, "Test", null, "java/lang/Object", null);

MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "m", "()V", null, null);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitFieldInsn(GETSTATIC, "MyClass", "x", "I"); // looking for int field
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();

Class<?> cls = lookup().defineClass(cw.toByteArray());
cls.getMethod("m").invoke(null); // NoSuchFieldError

Приведенный выше код работает и печатает 10, если поле имеет тип int, но завершается неудачно, если его изменить на byte.

...