Перейти к содержимому

Numberutils java как подключить

  • автор:

Как подключить arrayutils java

Теперь вы можете использовать методы ArrayUtils в своем коде.

int[] nums = 1, 2, 3, 4, 5>; int[] newNums = ArrayUtils.removeElement(nums, 3); // [1, 2, 3, 5] 

В приведенном выше примере метод removeElement удаляет значение 3 из массива nums , и новый массив без этого элемента сохраняется в переменную newNums

Как обработать более двух чисел в методе Math.max?

Начал учить Java, у Хорстманна в задании требуется с помощью метода Math.max определить наибольшее из 3-х введенных чисел, но насколько я понял, метод может сравнивать только 2 числа, или я чего-то не допонял?

  • Вопрос задан более трёх лет назад
  • 1048 просмотров

Комментировать

Решения вопроса 1

Sanan07

Писатель-прозаик

Да вы правы, но принимает два аргумента, но деллается задание по другому. int max = Math.max(Math.max(a,b),c); т.е. находится максимум двух чисел, и далее этот находим наибольшее среди максимума и третьего числа.

Ответ написан более трёх лет назад

Комментировать

Нравится 2 Комментировать

Как определить, является ли строка числом?

У меня тоже работает. Я просто про объявление throws NumberFormatException — по-моему оно лишнее, т.к. это исключение из функции никогда не вылетит.

2 дек 2011 в 22:39

Еще можно строку подвергнуть trim(), чтобы отсечь пробелы в начале и в конце (если это, конечно, не принципиально). По поводу имени функции — согласен с @Kremchik

2 дек 2011 в 23:03

Тут главный выбор из предложенных вариантов — выбор по скорости работы. Сделайте тестовую программку, прогоните эту функцию в разных вариантах много-много раз и проанализируйте время выполнения программы.

2 дек 2011 в 23:08

9 ответов 9

Сортировка: Сброс на вариант по умолчанию

Я так понимаю, цель именно на Integer проверить? Тогда можно:

s.matches("[-+]?\\d+") // для списка из 1 млн целых чисел и не чисел // (примерно 50/50) эффективность этой строчки практически // не отличается от описанной в вопросе функции. 

Если double , но без экспоненциальной нотации, то

((-|\\+)?[0-9]+(\\.[0-9]+)?)+ 

такое регулярное подойдёт.

Хотя я бы не парился и сделал бы точно так же, только с Double.parseDouble(s) , но я не показатель 🙂 И ещё я обратил внимание на название метода — оно не очень корректное, т.к. во-первых, такой есть в классе Character , во-вторых, по смыслу подходит isNumeric() . Но это просто комментарии из серии «что я думаю по этому поводу».

Отслеживать
ответ дан 2 дек 2011 в 22:58
2,828 14 14 серебряных знаков 18 18 бронзовых знаков
8 апр 2016 в 14:23
Проверка выражения является ли 2-1+1 числом, даёт положительный ответ.
24 сен 2019 в 6:06
Это же логично. Разве нет?
24 сен 2019 в 9:47
Спасибо, мне помогло!
14 мая 2021 в 11:12

Есть очень хороший static-метод в commons-lang (класс NumberUtils ), который учитывает множество особенностей чисел Java:

/** * 

Checks whether the String a valid Java number.

* *

Valid numbers include hexadecimal marked with the 0x * qualifier, scientific notation and numbers marked with a type * qualifier (e.g. 123L).

* *

Null and empty String will return * false.

* * @param str the String to check * @return true if the string is a correctly formatted number */ public static boolean isNumber(String str)

Отслеживать
ответ дан 8 апр 2016 в 13:41
Evgeny Lazarev Evgeny Lazarev
1,120 1 1 золотой знак 8 8 серебряных знаков 14 14 бронзовых знаков

Метод, описанный в вопросе, и есть самый правильный метод.

Методы, которые проверяют, состоит ли строка лишь из цифр, не справятся с переполнением (попробуйте число 100000000000000000000000000000000 ). Правильный паттерн для целого числа такой:

 ^(-?([1-9][0-9] |1[0-9] |20[0-9] |21[0-3][0-9] |214[0-6][0-9] |2147[0-3][0-9] |21474[0-7][0-9] |214748[0-2][0-9] |2147483[0-5][0-9] |21474836[0-3][0-9] |214748364[0-7] ) |-2147483648 |0)$ 

и поверьте, вам не хочется отлаживать его или адаптировать для других типов.

При этом решения, основанные на паттернах, не будут учитывать локаль и разрешённые разделители разрядов. Так что попробовать распарсить и поймать исключение — практически единственно правильное решение.

Отслеживать
ответ дан 8 апр 2016 в 15:02
207k 28 28 золотых знаков 293 293 серебряных знака 526 526 бронзовых знаков
8 апр 2016 в 15:26

@Qwertiy: Ну, мало ли какие баги и где. Писать кривые велосипеды к багам отдельной версии отдельной платформы — неправильно.

8 апр 2016 в 15:27
Использовать исключения для стандартной логики — неправильно 🙂
8 апр 2016 в 15:33

@Qwertiy: Да, именно для этого и нужен этот метод — заврапить дыру в дизайне платформы. В .NET, например, есть честный int.TryParse .

8 апр 2016 в 15:34

@Qwertiy: Но создавать по сути два отдельных парсера int ‘а и молиться Гослингу, чтобы они оказались консистентными — очень неправильно. Логика разбора int из строки должна быть лишь в одном месте, и это место — Integer.parseInt .

8 апр 2016 в 15:36

В связи с повышенным интересом по данному вопросу, сделал маленькое исследование 🙂

В исследовании принимали участия классы:

CharacterDelegator() // код @Sergey ComplexMatcher() // код @VladD NumberUtilsDelegator() // мой код SimpleMatcher() // код @ivkremer (для простых чисел) GuavaDelegator() // код @Nofate♦ SimpleMatcherWithDot() // код @ivkremer (для чисел с точкой) SimpleParser() // оригинальный код от @pol GuavaComplexDelegator() // модифицированный вариант кода @Nofate♦ для Float InnerSetImpl() // мой специфический вариант 

(@VladD, извиняюсь, понимаю, что делает ваш регэксп, но завести его у меня не получилось)

Вывода много, поэтому вкратце:

  • абсолютно все строки из примера распарсил только NumberUtils (commons-lang)
  • вариант с использованием Guava (расширенный) справился со всем кроме long-нотаций («l») и «0xCAFEBABE» 🙂 (честно, не понимаю, почему он варианты с «f»-нотацией прожевал)
  • остальные варианты в большей степени рассчитаны на парсинг именно интов, хотя в изначальном вопросе об этом ни слова 🙂

А вот самое интересное — это время работы данного кода.

Start performance test for core.impl.CharacterDelegator Ints: 125ms Numbers: 67ms Numbers with 25% errors: 50ms Small Ints: 43ms

Start performance test for core.impl.ComplexMatcher Ints: 10825ms Numbers: 11134ms Numbers with 25% errors: 10606ms Small Ints: 10380ms

Start performance test for core.impl.InnerSetImpl Ints: 50ms Numbers: 52ms Numbers with 25% errors: 54ms Small Ints: 42ms

Start performance test for core.impl.NumberUtilsDelegator Ints: 111ms Numbers: 91ms Numbers with 25% errors: 99ms Small Ints: 51ms

Start performance test for core.impl.SimpleMatcher Ints: 1072ms Numbers: 853ms Numbers with 25% errors: 847ms Small Ints: 766ms

Start performance test for core.impl.GuavaDelegator Ints: 131ms Numbers: 108ms Numbers with 25% errors: 124ms Small Ints: 119ms

Start performance test for core.impl.SimpleMatcherWithDot Ints: 3069ms Numbers: 5855ms Numbers with 25% errors: 5484ms Small Ints: 2548ms

Start performance test for core.impl.SimpleParser Ints: 157ms Numbers: 2189ms Numbers with 25% errors: 2117ms Small Ints: 81ms

Start performance test for core.impl.GuavaComplexDelegator Ints: 980ms Numbers: 943ms Numbers with 25% errors: 1016ms Small Ints: 837ms

Тест построен следующим образом

    Генерируем 2 рандомных списка со стрингами (числа). Тут есть 4 варианта:

 1. Просто Инты. 2. Числа (с 50% вероятностью в стринге есть точка). 3. Числа с возможной ошибкой (25% что в строке есть подстрока "error"). 4. Набор Интов в небольшом диапазоне. 

Из приведенных выкладок видно, что NumberUtils работает быстрее всего. Схожее время работы у простого варианты Guava`ы и простых regexp. Даже добавление простой точки значительно замедляет код. Также замечу, что код @Sergey работает очень быстро, но он рассчитан на проверку строго интов.

А еще есть мой специфический пример InnerSetImpl . Он основан на допущении, что у нас есть ограниченное число возможных вариантов (то есть, мы можем и готовы держать их в памяти). Тогда мы просто помещаем их в HashSet и проверяем наличие строк в нем. Собственно, такой вариант самый быстрый, но допущение многое портит 🙂

ИТОГО: если нужно простое и элегантное решение — то лучше всего воспользоваться NumberUtils.isNumber(str) и не париться.

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

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

Dagger 2. Часть третья. Новые грани возможного

Всем привет! Наконец-то подоспела третья часть цикла статей о Dagger 2!

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

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

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

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

Итак, хватит разглагольствовать, и вперед к новым знаниям!

Qualifier annotation

В прошлой статье в комментариях попросили осветить данный вопрос. Не будем откладывать в долгий ящик.

Часто бывает, что нам необходимо провайдить несколько объектов одного типа. Например, мы хотим иметь в системе два Executor : один однопоточный, другой с CachedThreadPool . В этом случае нам приходит на помощь «qualifier annotation». Это кастомная аннотация, которая имеет в себе аннотацию @Qualifier . Звучит немного как масло масляное, но на примере все гораздо проще.

В общем, Dagger2 предоставляет нам уже одну готовую «qualifier annotation», которой, пожалуй, вполне достаточно в повседневной жизни:

@Qualifier @Documented @Retention(RUNTIME) public @interface Named < /** The name. */ String value() default ""; >

А теперь посмотрим, как это все выглядит в бою:

Qualifier annotation пример

@Module public class AppModule < @Provides @Singleton @Named("SingleThread") public Executor provideSingleThreadExecutor() < return Executors.newSingleThreadExecutor(); >@Provides @Singleton @Named("MultiThread") public Executor provideMultiThreadExecutor() < return Executors.newCachedThreadPool(); >> public class MainActivity extends AppCompatActivity < @Inject @Named("SingleThread") Executor singleExecutor; @Inject @Named("MultiThread") Executor multiExecutor; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); MyApplication.getInstance().getAppComponent().inject(this); setContentView(R.layout.activity_main); >>

В итоге у нас два разных экземпляра ( singleExecutor , multiExecutor ) одного класса ( Executor ). То, что нам и нужно! Замечу, что объекты одного класса с аннотацией @Named могут провайдиться также как с абсолютно разных и независимых компонентов, так и c зависимых друг от друга.

Отложенная инициализация

Одна из распространенных наших разработческих проблем — это долгий старт приложения. Обычно причина в одном — мы слишком много всего грузим и инициализируем при старте. Кроме того, Dagger2 строит граф зависимостей в основном потоке. И часто далеко не все конструируемые Даггером объекты нужны сразу же. Поэтому библиотека дает нам возможность отложить инициализацию объекта до первого вызова с помощью интерфейсов Provider<> и Lazy<> .

Сразу же обратим наш взор на пример:

Пример отложенной инициализации

@Module public class AppModule < @Provides @Named("SingleThread") public Executor provideSingleThreadExecutor() < return Executors.newSingleThreadExecutor(); >@Provides @Named("MultiThread") public Executor provideMultiThreadExecutor() < return Executors.newCachedThreadPool(); >> public class MainActivity extends AppCompatActivity < @Inject @Named("SingleThread") ProvidersingleExecutorProvider; @Inject @Named("MultiThread") Lazy multiExecutorLazy; @Inject @Named("MultiThread") Lazy multiExecutorLazyCopy; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); MyApplication.getInstance().getAppComponent().inject(this); setContentView(R.layout.activity_main); // Executor singleExecutor = singleExecutorProvider.get(); Executor singleExecutor2 = singleExecutorProvider.get(); // Executor multiExecutor = multiExecutorLazy.get(); Executor multiExecutor2 = multiExecutorLazy.get(); Executor multiExecutor3 = multiExecutorLazyCopy.get(); >>

Начнем с Provider singleExecutorProvider . До первого вызова singleExecutorProvider.get() Даггер не инициализирует соответствующий Executor . Но при каждом последующем вызове singleExecutorProvider.get() будет создаваться новый экземпляр. Таким образом singleExecutor и singleExecutor2 — это два разных объекта. Такое поведение по сути идентично поведению unscoped объекта.

В каких вообще ситуациях уместен Provider ? Он пригождается, когда мы провайдим какую-то мутабельную зависимость, меняющую свое состояние в течении времени, и при каждом обращении нам необходимо получать актуальное состояние. «Что за кривая архитектура?» — скажите вы, и я с вами соглашусь. Но при работе с legacy кодом и не такое увидишь.

Отмечу, что авторы библиотеки тоже не советуют злоупотреблять интерфейсом Provider в тех местах, где достаточно обойтись обычным unscope, так как это чревато «кривой архитектурой», как говорилось выше, и трудно отлавливаемыми багами.

Теперь Lazy multiExecutorLazy и Lazy multiExecutorLazyCopy . Dagger2 инициализирует соответствующие Executor только при первом вызове multiExecutorLazy.get() и multiExecutorLazyCopy.get() . Далее Даггер кэширует проинициализированные значения для каждого Lazy<> и при втором вызове multiExecutorLazy.get() и multiExecutorLazyCopy.get() выдает закэшированные объекты.

Таким образом multiExecutor и multiExecutor2 ссылаются на один объект, а multiExecutor3 на второй объект.

Но, если мы в AppModule к методу provideMultiThreadExecutor() добавим аннотацию @Singleton , то объект будет кешироваться для всего дерева зависимостей, и multiExecutor , multiExecutor2 , multiExecutor3 будут ссылаться на один объект.

Асинхронная загрузка

Мы подошли с вами к весьма нетривиальной задаче. А что, если мы хотим, чтобы конструирование графа зависимостей проходило в бэкграунде? Звучит многообещающе? Да-да, я про Producers.

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

Плюсы. Ну самый главный плюс — это загрузка в бэкграунде и возможность управлять этим процессом загрузки.

Минусы. Producers «тащат» за собой Guava, а это плюс 15 тысяч методов к апк. Но самое плохое, что применение Producers немного «портят» общую архитектуру и делают код более запутанным. Если у вас уже был Даггер, а потом вы решили перенести инициализацию объектов в бэкграунд, вам придется хорошенько постараться.

В официальной документации данная темы выделена в специальный раздел. Но я очень рекомендую статьи Miroslaw Stanek. У него вообще очень хороший блог, и там много статей про Dagger2. Собственно, некоторые даже макеты картинок с прошлых статей я заимствовал у него.
Про Producers он пишет в этой статье.

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

Один только минус: Мирослав не совсем верно применяет Observable.create(. ) . Но я об этом написал в комментарии к статье, так что обратите внимание обязательно.

А теперь посмотрим, как будет выглядеть тогда код для scope объекта (с «правильной» RxJava):

Пример со scope

@Module public class AppModule < @Provides @Singleton // or custom scope for "local" singletons HeavyExternalLibrary provideHeavyExternalLibrary() < HeavyExternalLibrary heavyExternalLibrary = new HeavyExternalLibrary(); heavyExternalLibrary.init(); //This method takes about 500ms return heavyExternalLibrary; >@Provides @Singleton // or custom scope for "local" singletons Observable provideHeavyExternalLibraryObservable( final Lazy heavyExternalLibraryLazy) < return Observable.fromCallable(heavyExternalLibraryLazy::get) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); >> public class MainActivity extends AppCompatActivity < @Inject ObservableheavyExternalLibraryObservable; //This will be injected asynchronously HeavyExternalLibrary heavyExternalLibrary; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); MyApplication.getInstance().getAppComponent().inject(this); setContentView(R.layout.activity_main); // init HeavyExternalLibrary in background thread! heavyExternalLibraryObservable.subscribe( heavyExternalLibrary1 ->heavyExternalLibrary = heavyExternalLibrary1, throwable -> <> ); > >

Обратите внимание на @Singleton и интерфейс Lazy в AppModule . Lazy как раз и гарантирует, что тяжеловесный объект будет проинициализирован, когда мы запросим, а затем закеширован.

А как нам быть, если мы хотим каждый раз получать новый экземпляр этого «тяжелого» объекта? Тогда стоит немного поменять AppModule :

Пример с unscope

@Module public class AppModule < @Provides // No scope! HeavyExternalLibrary provideHeavyExternalLibrary() < HeavyExternalLibrary heavyExternalLibrary = new HeavyExternalLibrary(); heavyExternalLibrary.init(); //This method takes about 500ms return heavyExternalLibrary; >@Provides @Singleton // or custom scope for "local" singletons Observable provideHeavyExternalLibraryObservable( final Provider heavyExternalLibraryLazy) < return Observable.fromCallable(heavyExternalLibraryLazy::get) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); >> public class MainActivity extends AppCompatActivity < @Inject ObservableheavyExternalLibraryObservable; //This will be injected asynchronously HeavyExternalLibrary heavyExternalLibrary; HeavyExternalLibrary heavyExternalLibraryCopy; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); MyApplication.getInstance().getAppComponent().inject(this); setContentView(R.layout.activity_main); // init HeavyExternalLibrary and heavyExternalLibraryCopy in background thread! heavyExternalLibraryObservable.subscribe( heavyExternalLibrary1 ->heavyExternalLibrary = heavyExternalLibrary1, throwable -> <> ); heavyExternalLibraryObservable.subscribe( heavyExternalLibrary1 -> heavyExternalLibraryCopy = heavyExternalLibrary1, throwable -> <> ); > >

Для метода provideHeavyExternalLibrary() мы убрали scope, а в provideHeavyExternalLibraryObservable(final Provider heavyExternalLibraryLazy) используем Provider вместо Lazy . Таким образом heavyExternalLibrary и heavyExternalLibraryCopy в MainActivity — это разные объекты.

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

SplashActivity со статьи Мирослава

public class SplashActivity extends BaseActivity < @Inject SplashActivityPresenter presenter; @Inject AnalyticsManager analyticsManager; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setupActivityComponent(); >@Override protected void setupActivityComponent() < final SplashActivityComponent splashActivityComponent = GithubClientApplication.get(SplashActivity.this) .getAppComponent() .plus(new SplashActivityModule(SplashActivity.this)); splashActivityComponent.inject(SplashActivity.this); >>

А теперь взглянем на обновленный метод void setupActivityComponent() (с моими правками по RxJava):

void setupActivityComponent()

@Override protected void setupActivityComponent() < Completable.fromAction(() ->< final SplashActivityComponent splashActivityComponent = GithubClientApplication.get(SplashActivity.this) .getAppComponent() .plus(new SplashActivityModule(SplashActivity.this)); splashActivityComponent.inject(SplashActivity.this); >) .doOnCompleted(() -> < //Here is the moment when injection is done. analyticsManager.logScreenView(getClass().getName()); presenter.callAnyMethod(); >) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(() -> <>, throwable -> <>); >

Замеры

В прошлом разделе мы говорили про производительность при старте приложения. Однако мы знаем, что, если вопрос касается производительности и скорости, мы должны замерять! Полагаться на интуицию и чувство «вроде бы стало быстрее» нельзя. И с этим нам снова поможет Мирослав в этой и этой статьях. Чтобы мы все делали без него, вообще не представляю.

Новые интересные возможности

У Даггера появляются новые интересные фичи, обещающие нам облегчить жизнь. Но вот понять, как все работает и что же нам это все дает, — было задачей не из легких. Ну что же, начнем!

@Reusable scope

Интересная аннотация. Позволяет экономить память, но при этом по сути не ограничена никаким scope , что делает очень удобным переиспользование зависимостей в любых компонентах. То есть это нечто среднее между scope и unscope .

В доках пишут очень важный момент, который как-то не бросается в глаза с первого раза: «Для каждого компонента, который использует @Reusable зависимость, данная зависимость кешируется отдельно«. И мое дополнение: «В отличии от scope аннотации, где объект кешируется при создании и его экземпляр используется дочерними и зависимыми компонентами«.

А теперь сразу пример, чтобы все понять:

Длинный пример с разъяснениями

Наш главный компонент.

@Component(modules = ) @Singleton public interface AppComponent

У AppComponent есть два Subcomponent . Обратили внимание на эту конструкцию — FirstComponent.Builder ? О ней мы чуть позже.
Теперь посмотрим на UtilsModule .

@Module public class UtilsModule < @Provides @NonNull @Reusable public NumberUtils provideNumberUtils() < return new NumberUtils(); >@Provides @NonNull public StringUtils provideStringUtils() < return new StringUtils(); >>

NumberUtils с аннотацией @Reusable , а StringUtils оставим unscoped .
Далее у нас два Subcomponents .

@FirstScope @Subcomponent(modules = FirstModule.class) public interface FirstComponent < @Subcomponent.Builder interface Builder < FirstComponent.Builder firstModule(FirstModule firstModule); FirstComponent build(); >void inject(MainActivity mainActivity); > @SecondScope @Subcomponent(modules = ) public interface SecondComponent < @Subcomponent.Builder interface Builder < SecondComponent.Builder secondModule(SecondModule secondModule); SecondComponent build(); >void inject(SecondActivity secondActivity); void inject(ThirdActivity thirdActivity); >

Как мы видим, FirstComponent инжектирует только в MainActivity , а SecondComponent — в SecondActivity и ThirdActivity .
Посмотрим код.

public class MainActivity extends AppCompatActivity < @Inject NumberUtils numberUtils; @Inject StringUtils stringUtils; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyApplication.getInstance().getFirstComponent() .inject(this); // other. >> public class SecondActivity extends AppCompatActivity < @Inject NumberUtils numberUtils; @Inject StringUtils stringUtils; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); MyApplication.getInstance().getSecondComponent() .inject(this); // other. >> public class ThirdActivity extends AppCompatActivity < @Inject NumberUtils numberUtils; @Inject StringUtils stringUtils; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_third); MyApplication.getInstance().getSecondComponent() .inject(this); // other. >>

Коротко про навигацию. Из MainActivity мы попадаем в SecondActivity , а затем в ThirdActivity . А теперь вопрос. Когда мы будем уже на третьем экране, сколько объектов NumberUtils и StringUtils будет создано?

Так как StringUtils — unscoped , то будет создано три экземпляра, то есть при каждой инъекции создается новый объект. Это мы знаем.

А вот объектов NumberUtils будет два — один для FirstComponent , а другой для SecondComponent . И здесь я снова приведу основную мысль про @Reusable с документации: «Для каждого компонента, который использует @Reusable зависимость, данная зависимость кешируется отдельно!«, в отличии от scope аннотации, где объект кешируется при создании и его экземпляр используется дочерними и зависимыми компонентами.

Но сами гугловцы предупреждают, что если вам необходим уникальный объект, который может быть еще и mutable, то используйте только scoped аннотации.

Еще приведу ссылку на вопрос про сравнение @Singleton и @Reusable со SO.

@Subcomponent.Builder

Фича, которая делает код красивее. Раньше, чтобы создать @Subcomponent нам приходилось писать нечто такое:

Как было

@Component(modules = ) @Singleton public interface AppComponent < FirstComponent plusFirstComponent(FirstModule firstModule, SpecialModule specialModule); >@FirstScope @Subcomponent(modules = ) public interface FirstComponent
appComponent .plusFirstComponent(new FirstModule(), new SpecialModule());

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

Как стало

@Component(modules = ) @Singleton public interface AppComponent < FirstComponent.Builder firstComponentBuilder(); >@FirstScope @Subcomponent(modules = ) public interface FirstComponent < @Subcomponent.Builder interface Builder < FirstComponent.Builder firstModule(FirstModule firstModule); FirstComponent.Builder specialModule(SpecialModule specialModule); FirstComponent build(); >void inject(MainActivity mainActivity); >

Создание FirstComponent теперь выглядит следующим образом:

appComponent .firstComponentBuilder() .firstModule(new FirstModule()) .specialModule(new SpecialModule()) .build();

static

Теперь у нас есть возможность делать вот так:

@Provides static User currentUser(AuthManager authManager)

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

На SO задали хороший вопрос на эту тему, мол, а чем собственно отличаются @Singleton от @Provide static . Чтобы хорошо понять эту разницу, нужно читать ответ на вопрос, параллельно экспериментируя и смотря сгенерированный код.

Итак, у нас есть вводная. Мы имеем три варианта одного и того же метода в модуле:

@Provides User currentUser(AuthManager authManager) < return authManager.currentUser(); >@Provides @Singleton User currentUser(AuthManager authManager) < return authManager.currentUser(); >@Provides static User currentUser(AuthManager authManager)

При этом authManager.currentUser() в разные моменты времени может отдавать разные экземпляры.
Логичный вопрос: а чем эти методы отличаются.

В первом случае у нас классический unscope . При каждом запросе будет отдаваться новый экземпляр authManager.currentUser() (точнее новая ссылка на currentUser ).

Во втором случае при первом запросе будет закеширована ссылка на currentUser , и при каждом новом запросе будет отдаваться эта ссылка. То есть, если поменялся currentUser в AuthManager , то отдаваться то будет старая ссылка на невалидный уже экземпляр.

Третий случай уже интереснее. Данный метод по поведению аналогичен unscope , то есть при каждом запросе будет отдаваться новая ссылка. Это первое отличие от @Singleton , который кеширует объекты. Таким образом размещать в @Provide static методе инициализацию объекта не совсем уместно.

Но в чем тогда @Provide static отличается от unscope ? Допустим у нас есть такой модуль:

@Module public class AuthModule < @Provides User currentUser(AuthManager authManager) < return authManager.currentUser(); >>

AuthManager поставляется из другого модуля в качестве Singleton . Теперь быстро окинем взглядом сгенерированный код AuthModule_CurrentUserFactory (в студии просто поставьте курсор на currentUser и нажмите Ctrl+B):

Unscope

@Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.github.io/dagger" ) public final class AuthModule_CurrentUserFactory implements Factory  < private final AuthModule module; private final ProviderauthManagerProvider; public AuthModule_CurrentUserFactory( AuthModule module, Provider authManagerProvider) < assert module != null; this.module = module; assert authManagerProvider != null; this.authManagerProvider = authManagerProvider; >@Override public User get() < return Preconditions.checkNotNull( module.currentUser(authManagerProvider.get()), "Cannot return null from a non-@Nullable @Provides method"); >public static Factory create(AuthModule module, Provider authManagerProvider) < return new AuthModule_CurrentUserFactory(module, authManagerProvider); >/** Proxies . */ public static User proxyCurrentUser(AuthModule instance, AuthManager authManager) < return instance.currentUser(authManager); >>

А если добавить static к currentUser :

@Module public class AuthModule < @Provides static User currentUser(AuthManager authManager) < return authManager.currentUser(); >>

static

@Generated( value = "dagger.internal.codegen.ComponentProcessor", comments = "https://google.github.io/dagger" ) public final class AuthModule_CurrentUserFactory implements Factory  < private final ProviderauthManagerProvider; public AuthModule_CurrentUserFactory(Provider authManagerProvider) < assert authManagerProvider != null; this.authManagerProvider = authManagerProvider; >@Override public User get() < return Preconditions.checkNotNull( AuthModule.currentUser(authManagerProvider.get()), "Cannot return null from a non-@Nullable @Provides method"); >public static Factory create(Provider authManagerProvider) < return new AuthModule_CurrentUserFactory(authManagerProvider); >/** Proxies . */ public static User proxyCurrentUser(AuthManager authManager) < return AuthModule.currentUser(authManager); >>

Обратите внимание, что в варианте со static нет AuthModule . Таким образом, статический метод дергается компонентом напрямую, минуя модуль. А если в модуле только одни статические методы, то экземпляр модуля даже не создается.

Экономия и минус лишние вызовы. Собственно у нас выигрыш по производительности. Также пишут, что вызов статического метода на 15-20% быстрее вызова аналогичного нестатического метода. Если я ошибаюсь, iamironz поправит меня. Уж он то точно знает, а если нужно, и замерит.

@Binds + Inject конструктора

Мегаудобная связка, которая значительно уменьшает boilerplate-code. На заре изучения Даггера я не понимал, зачем нужны инъекции конструктора. Что и откуда берется. А тут еще появился @Binds. Но все на самом деле довольно просто. Спасибо за помощь Владимиру Тагакову и вот этой статье.

Рассмотрим типичную ситуацию. Есть интерфейс Презентера и его реализация:

public interface IFirstPresenter < void foo(); >public class FirstPresenter implements IFirstPresenter < public FirstPresenter() <>@Override public void foo() <> >

Мы, как белые люди, провайдим все это дело в модуле и инжектим интерфейс Презентера в активити:

@Module public class FirstModule < @Provides @FirstScope public IFirstPresenter provideFirstPresenter() < return new FirstPresenter(); >> public class MainActivity extends AppCompatActivity < @Inject IFirstPresenter firstPresenter; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyApplication.getInstance().getFirstComponent() .inject(this); // others >>

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

Модуль будет такой:

@Module public class FirstModule < @Provides @FirstScope public HelperClass1 provideHelperClass1() < return new HelperClass1(); >@Provides @FirstScope public HelperClass2 provideHelperClass2() < return new HelperClass2(); >@Provides @FirstScope public IFirstPresenter provideFirstPresenter( HelperClass1 helperClass1, HelperClass2 helperClass2) < return new FirstPresenter(helperClass1, helperClass2); >>

И так вот каждый раз, если нужно добавить какой-то класс и «расшарить» его другим. Модуль «загрязняется» очень быстро. И как-то слишком много кода, не находите? Но есть решение, которое существенно уменьшает код.

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

@FirstScope public class HelperClass1 < @Inject public HelperClass1() < >> @FirstScope public class HelperClass2 < @Inject public HelperClass2() < >>

Обратите внимание, что к классам была добавлена аннотация @FirstScope , таким образом Даггер понимает, в какое дерево зависимостей отнести данные классы.

Теперь с модуля мы можем смело убирать провайдинг HelperClass1 и HelperClass2 :

@Module public class FirstModule < @Provides @FirstScope public IFirstPresenter provideFirstPresenter( HelperClass1 helperClass1, HelperClass2 helperClass2) < return new FirstPresenter(helperClass1, helperClass2); >>

Как можно еще уменьшить код в модуле? Вот здесь применим @Binds :

@Module public abstract class FirstModule

А в FirstPresenter сделаем инъекцию конструктора:

@FirstScope public class FirstPresenter implements IFirstPresenter < private HelperClass1 helperClass1; private HelperClass2 helperClass2; @Inject public FirstPresenter(HelperClass1 helperClass1, HelperClass2 helperClass2) < this.helperClass1 = helperClass1; this.helperClass2 = helperClass2; >@Override public void foo() <> >

Какие здесь новшества? FirstModule стал у нас абстрактным, как и метод provideFirstPresenter . У provideFirstPresenter убрали аннотацию @Provide , зато добавили @Binds . А в аргументы передаем не необходимые зависимости, а конкретную реализацию!
У FirstPresenter добавилась scope аннотация — @FirstScope , по которой Даггер понимает, куда отнести данный класс. Также к конструктору добавили аннотацию @Inject . Стало намного чище, и добавлять новые зависимости стало еще проще!

Пару ценных дополнений по абстрактным модулям от Mujahit.
Давайте вспомним, что FirstModule относится к FirstComponent , который в свою очередь является сабкомпонентом от AppComponent . И чтобы создать FirstComponent мы делали вот так:

appComponent .firstComponentBuilder() .firstModule(new FirstModule()) .specialModule(new SpecialModule()) .build();

Но как нам создать то экземпляр FirstModule , если он является абстрактным? В прошлых статья я упоминал, что если мы в конструктор модулей ничего не передаем, то есть используем конструкторы по умолчанию, то при создании компонента инициализацию этих модулей можно опустить:

appComponent .firstComponentBuilder() .build();

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

Также замечу, что если у модуля одни абстрактные методы, то модуль можно реализовать через интерфейс:

@Module public interface FirstModule

Кроме того в абстрактный модуль мы можем также добавить только статические методы. «Обычные» методы добавить не можем:

@Module public abstract class FirstModule < @FirstScope @Binds public abstract IFirstPresenter provideFirstPresenter(FirstPresenter firstPresenter); @FirstScope @Provides public HelperClass3 provideHelperClass3() < // @FirstScope @Provides public static HelperClass3 provideHelperClass3() < // >

О чем еще не сказано

Далее я приведу еще список фич с коротким описанием и ссылками на качественное объяснение:

  1. Muitibindings. Позволяет «байндить» объекты в коллекции ( Set и Map ). Подходит для реализации архитектуры расширения («plugin architecture»). Крайне рекомендую вот это очень подробное описание с азов. Более интересные примеры применения Muitibindings можно найти в статьях Мирослава тут и тут. И еще в придачу ссылка на официальную документацию. Так что мне даже нечего добавить по данному вопросу.
  2. Releasable references. Если уж с памятью совсем беда. С помощью соответствующих аннотаций мы помечаем объекты, которыми можем пожертвовать при недостатке памяти. Вот такой вот хак.
    В доках (подраздел Releasable references) вполне все понятно описано, как ни странно.
  3. Тестирование. Конечно же, для Unit-тестирования Даггер не нужен. А вот для функциональных, интеграционных и UI тестов может пригодиться возможность подмены определенных модулей. Очень здорово эту тему раскрывает Artem_zin в своей статье и примере. В документации выделен раздел по вопросу тестирования. Но опять-таки гугловцы не могут нормально описать, как именно подменить компонент. Как правильно создать фэйковые модули и подставить их. Для подмены компонента (отдельных модулей) я пользуюсь способом Артема. Да, хотелось бы, чтобы можно было создать отдельным классом тестовый компонент и отдельными классами тестовые модули, и красиво все это подключить в тестовом Application файле. Может кто знает?
  4. @BindsOptionalOf. Работает вместе с Optional от Java 8 или Guava, что делает данную фичу уже труднодоступной для нас. Если интересно, в конце документации можно найти описание.
  5. @BindsInstance. К сожалению, в dagger 2.8 мне данная фича оказалась недоступной. Основной посыл ее в том, что хватит передавать какие-либо объекты через конструктор модуля. Очень распространенный пример, когда через конструктор AppComponent передается глобальный Context . Так вот с этой аннотацией такого делать станет не нужно. В конце документации есть пример.

Ну вот и все! Вроде все моменты удалось осветить. Если что-то пропустил или недостаточно описал, пишите! Исправим. Также рекомендую группу по Dagger2 в Телеграме, где ваши вопросы не останутся без ответов.

Кроме того, правильное применение библиотеки очень связано с чистой архитектурой. Поэтому вот вам и группа по архитектуре. И да, скоро на AndroidDevPodcast планируется выпуск, посвященный Даггеру. Следите за новостями!

  • android
  • android development
  • dagger 2
  • java
  • architecture design
  • mobile development
  • Java
  • Разработка мобильных приложений
  • Разработка под Android

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *