LINUX.ORG.RU

Поясните за Яву - зачем используют массив строк для одной строки?

 ,


1

3

В ходе исправления бага:

https://bugreports.qt.io/browse/QTBUG-68813

Разработчик фреймверка написал такой код:

https://codereview.qt-project.org/#/c/252757/2/src/android/jar/src/org/qtproj...

В нем есть такая функция:

    public static String loadMainLibrary(final String mainLibrary, final String nativeLibraryDir)
    {
        final String[] res = new String[1];
        res[0] = null;
        m_qtThread.run(new Runnable() {
            @Override
            public void run() {
                try {
                    String mainLibNameTemplate = "lib" + mainLibrary + ".so";
                    File f = new File(nativeLibraryDir + mainLibNameTemplate);
                    if (!f.exists()) {
                        try {
                            ActivityInfo info = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(),
                                    PackageManager.GET_META_DATA);
                            String systemLibraryDir = QtNativeLibrariesDir.systemLibrariesDir;
                            if (info.metaData.containsKey("android.app.system_libs_prefix"))
                                systemLibraryDir = info.metaData.getString("android.app.system_libs_prefix");
                            f = new File(systemLibraryDir + mainLibNameTemplate);
                        } catch (Exception e) {
                            e.printStackTrace();
                            return;
                        }
                    }
                    if (!f.exists())
                        return;
                    System.load(f.getAbsolutePath());
                    res[0] = f.getAbsolutePath();
                } catch (Exception e) {
                    Log.e(QtTAG, "Can't load '" + mainLibrary + "'", e);
                }
            }
        });
        return res[0];
    }

Вопрос. Зачем он вместо создания строки создает массив строк, а потом возвращает первый элемент?

★★★★★

Если Runnable выполняется в отдельном потоке и m_qtThread.run() не блокирует исполнение текущего потока то написан бред, потому как этот метод всегда будет возвращать null. Массив тут костыль. В Java из замыканий можно получить доступ только к неизменяемым локальным переменным (с модификатором final), но к final String уже нельзя будет присвоить другую строку, как и ссылку на final массив тоже нельзя поменять, но содержимое final массива менять можно.

Aber ★★★★★
()
Последнее исправление: Aber (всего исправлений: 1)
Ответ на: комментарий от Aber

То есть, тут проблема, что у явовских замыканий нет возможности мутабельности как в C++, и ее обходят использованием массива? В этом костыль состоит?

Xintrea ★★★★★
() автор топика

Вопрос. Зачем он вместо создания строки создает массив строк, а потом возвращает первый элемент?

Потому что он хреновый программист.

urxvt ★★★★★
()
Ответ на: комментарий от urxvt

Потому что он хреновый программист.

Ну уж получше, чем ты

annulen ★★★★★
()

В замыкание можно передать только final (т. е. неизменяемую) переменную. А хочется всё же её изменить. В итоге передаётся массив из одного элемента и меняется его содержимое. Ссылка на сам массив при этом ведь не изменяется.

Это фактически костыль, который возникает в языках с управляемой памятью и отсутствием полноценных указателей.

C++:

int a;
int *b = &a;
...
*b = 10;

Java:

int[] b = new int[] { 0 };
...
b[0] = 10;

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

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 2)
Ответ на: комментарий от Xintrea

То есть, тут проблема, что у явовских замыканий нет возможности мутабельности как в C++,

Нет, если ты сишник ты знаешь про стек вызовов, что стек это один из механизмов автоматического управления памятью, локальные переменные определяются и хранятся в стеке, после выхода из вызываемой функции stack pointer смещается назад, локальные переменные в стеке будут уничтожены/перезаписаны, потому в СИ, как и в CPP, нельзя из функции вернуть ссылку на локальную переменную. Логикой можно предположить, что final переменные в java можно адресовать скопировав значение(ведь они не поменяются), в данном случае в анонимный класс копируется ссылка на массив строк, даже после выхода из метода loadMainLibrary, анонимый класс будет адресовать созданный в куче массив строк.

Aber ★★★★★
()

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

public class Main {
    public static void main(String[] args) throws InterruptedException {
        String res = "hello";
        new Thread(new Runnable() {
            @Override
            public void run() {
                res += " world";
            }
        }).start();
        Thread.sleep(100);
        System.out.println(res);
    }
}

Error:(8, 17) java: local variables referenced from an inner class must be final or effectively final
public class Main {

    public static void main(String[] args) throws InterruptedException {
        final String[] res = {"hello"};
        new Thread(new Runnable() {
            @Override
            public void run() {
                res[0] += " world";
            }
        }).start();
        Thread.sleep(100);
        System.out.println(res[0]);
    }
}
hello world
fsb4000 ★★★★★
()
Ответ на: комментарий от Aber

переменные в java можно адресовать скопировав значение

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

Aber ★★★★★
()
Ответ на: комментарий от fsb4000

Ещё чуток, чтобы закончить...

Как видно из ошибки, ограничение на final только для локальных переменных. Если у тебя поле класса, то всё ок и без final...

public class Main {
    static String res = "hello";
    public static void main(String[] args) throws InterruptedException {

        new Thread(new Runnable() {
            @Override
            public void run() {
                res += " world";
            }
        }).start();
        Thread.sleep(100);
        System.out.println(res);
    }
}

hello world
fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)
Ответ на: комментарий от Aber

Удвою насчет того, что будет возвращать null (строго говоря тут UB, т.к. гипотетически m_qtThread может отработать раньше, если другой тред заснет, а не вернет сразу null). Кроме того, вместо массива надо было использовать AtomicReference.

mythCreator
()
Ответ на: комментарий от mythCreator

Эксперты в треде?

mythCreator, Aber

Ещё раз, нет тут UB, и не вернёт null, так как поток вызывает run, а не start. То есть всё выполнение будет происходить в одном и том же потоке.

public class Main {
    static String res = "hello";
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                res += " world";
                try {
                    Thread.sleep(10000); // спим 10 секунд
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).run();
        System.out.println(res);
    }
}
hello world
fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

Эксперты в треде?

Да, эксперты.

Ещё раз, нет тут UB, и не вернёт null, так как поток вызывает run, а не start.

Я не смог найти исходники QtThread чтоб утверждать что run(Runnable runnable) не создаст отдельный поток. А если отдельный поток ненужен то тогда такая конструкция в коде не имеет смысла.

Если у тебя поле класса, то всё ок и без final...

Если эту статическую функцию loadMainLibrary вызовут два потока, то вернувшейся результат будет неопределенный. И хранение состояний в статических полях рано или поздно приведет к ошибкам которые не будут отлавливаться юнит тестами.

Aber ★★★★★
()
Ответ на: комментарий от KivApple

В замыкание можно передать только final (т. е. неизменяемую) переменную. А хочется всё же её изменить. В итоге передаётся массив из одного элемента и меняется его содержимое. Ссылка на сам массив при этом ведь не изменяется.

Это фактически костыль, который возникает в языках с управляемой памятью и отсутствием полноценных указателей.

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

Ты в одном посте описал два разных кейса. Один из них — явопроблемы.

(define (foo . args)
  (define result "")
  (for ((arg args))
    (thread
     (lambda ()
       (sleep (/ (random 1 10) 10))
       (set! result arg))))
  (sleep 1)
  result)

(for ((i (in-range 10)))
  (displayln (foo "foo" "bar" "gee")))
korvin_ ★★★★★
()

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

Кстати, строка res[0] = null; тут лишняя, поскольку по-умолчанию массив объектов инициализируется null-ами.

bbk123 ★★★★★
()
Ответ на: комментарий от bbk123

Кстати, строка res[0] = null; тут лишняя, поскольку по-умолчанию массив объектов инициализируется null-ами.

Да там вообще весь код бредовый. Запустили поток и тут же отдали результат, в котором на данный момент с большой вероятностью будет null лежать. Он походу этот код даже не запускал.

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Там не всё так просто, #run у треда QT имеет собственную имплементацию, внутри какие-то синхронизации и пул тредов. Поэтому этот поток выполнится до возврата из метода.

Короче руки за такое нужно отрывать, впрочем QT успешен, лучше никто не сделал.

foror ★★★★★
()
Ответ на: комментарий от Legioner

Я вначале тоже подумал о выходе с null, но это либо легаси, либо особенности обвязки нативного QT для Android.

foror ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.