Разбор флагов доступа к полю в Java - PullRequest
9 голосов
/ 17 мая 2011

У меня есть задание, в котором я должен проанализировать флаги доступа к полям файла Java .class. Спецификацию для файла .class можно найти здесь: Формат файла класса (на страницах 26 и 27 есть флаги доступа и шестнадцатеричные значения).

Это хорошо, я могу сделать это без забот. Моя проблема в том, что существует большое количество комбинаций.

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

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

Я думал сделать по модулю флаг доступа и соответствующее шестнадцатеричное значение для public, private или protected, но public - 0x0001, так что это не сработает.

Есть ли у кого-нибудь еще какие-либо идеи относительно того, как я мог бы уменьшить количество дел в своем заявлении о замене?

Ответы [ 3 ]

5 голосов
/ 17 мая 2011

В чем проблема?В спецификации сказано, что это битовый флаг, это означает, что вы должны смотреть на значение как двоичное число, и что вы можете проверить, установлено ли конкретное значение, выполнив побитовое AND.

Например

/*
ACC_VOLATILE = 0x0040 = 10000000
ACC_PUBLIC   = 0x0001 = 00000001
Public and volatile is= 10000001
*/


publicCount += flag & ACC_PUBLIC > 0 ? 1 : 0;
volatileCount += flag & ACC_VOLATILE > 0 ? 1 : 0;
5 голосов
/ 17 мая 2011

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

if (access_flag & ACC_PUBLIC != 0)
{
    public++;
}
if (access_flag & ACC_FINAL != 0)
{
    final++;
}
...

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

Таким образом, большая хитрость заключается в том, чтобы сделать этот доступ «универсальным» и легким для понимания из вызывающего класса - вытащить все повторяющиеся дерьма и просто оставить «мясо», перенести сложность в общую процедуру.

Таким образом, простой способ вызова метода - это что-то вроде этого, который дает массив битовых полей, которые содержат много битовых комбинаций, которые необходимо посчитать, и список полей, которые вас интересуют (чтобы вы не тратили время на тестирование). поля, которые вас не интересуют):

int[] counts = sumUpBits(arrayOfFlagBitfields, ACC_PUBLIC | ACC_FINAL | ACC_...);

Это действительно чисто, но тогда как вы получите доступ к полям возврата? Я изначально думал что-то вроде этого:

System.out.println("Number of public classes="+counts[findBitPosition(ACC_PUBLIC]));
System.out.println("Number of final classes="+counts[findBitPosition(ACC_FINAL)]);

Большая часть шаблонов здесь пропала, за исключением необходимости менять битовые поля в их положение. Я думаю, что два изменения могут сделать его лучше - инкапсулировать его в класс и использовать хеш для отслеживания позиций, чтобы вам не приходилось все время преобразовывать bitPosition (если вы предпочитаете не использовать хеш, findBitPosition находится в конце ).

Давайте попробуем полноценный класс. Как это должно выглядеть с точки зрения звонящего?

BitSummer bitSums=new BitSummer(arrayOfFlagBitfields, ACC_PUBLIC, ACC_FINAL);
System.out.println("Number of public classes="+bitSums.getCount(ACC_PUBLIC));
System.out.println("Number of final classes="+bitSums.getCount(ACC_FINAL));

Это довольно чисто и легко - я действительно люблю ОО! Теперь вы просто используете bitSums для хранения ваших значений до тех пор, пока они не понадобятся (это менее шаблонно, чем хранить их в переменных класса, и более понятно, чем при использовании массива или коллекции)

Так что теперь, чтобы закодировать класс. Обратите внимание, что конструктор теперь использует переменные аргументы - меньше удивления / более обычного и имеет больше смысла для реализации хэша.

Кстати, я знаю, что это кажется медленным и неэффективным, но, вероятно, это не плохо для большинства применений - если это так, его можно улучшить, но это должно быть намного короче и менее избыточно, чем коммутатор оператор (который на самом деле такой же, как этот, только что развернутый - однако этот использует хэш и автобокс, что повлечет за собой дополнительный штраф).

public class BitSummer {
    // sums will store the "sum" as <flag, count>
    private final HashMap<Integer, Integer> sums=new HashMap<Integer, Integer>();

    // Constructor does all the work, the rest is just an easy lookup.
    public BitSummer(int[] arrayOfFlagBitfields, int ... positionsToCount) {

        // Loop over each bitfield we want to count
        for(int bitfield : arrayOfFlagBitfields) {

            // and over each flag to check
            for(int flag : positionsToCount) {
                // Test to see if we actually should count this bitfield as having the flag set
                if((bitfield & flag) != 0) {
                    sums.put(flag, sums.get(flag) +1); // Increment value
                }
            }
        }
    }

    // Return the count for a given bit position
    public int getCount(int bit) {
        return sums.get(bit);
    }
}

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

Что касается поддержки кода, то он может выглядеть «длинным» по сравнению с исходным примером, но если у вас есть более 5 или 6 полей для проверки, это на самом деле будет более коротким решением, чем цепочечные операторы if, и значительно меньше ошибок / склонностей и более ремонтопригоден - тоже интереснее писать.

Если вы действительно чувствуете необходимость удалить хеш-таблицу, вы можете легко заменить ее разреженным массивом с позицией флага в качестве индекса (например, счетчик флага 00001000 / 0x08 будет сохранен в четвертой позиции массива). Для этого потребуется функция, подобная этой, для вычисления позиции бита для доступа к массиву (как сохранение в массиве, так и получение)

private int findBitPosition(int flag) {
    int ret;
    while( ( flag << 1 ) != 0 )
        ret++;
    return ret;
}

Это было весело.

2 голосов
/ 17 мая 2011

Я не уверен, что это то, что вы ищете, но я бы использовал if-case с двоичным AND, чтобы проверить, установлен ли флаг:

if (access_flag & ACC_PUBLIC != 0)
{
    // class is public
}
if (access_flag & ACC_FINAL != 0)
{
    // class is final
}
....
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...