Содержание

Kotlin. Массивы

Статья проплачена кошками — всемирно известными производителями котят.

Если статья вам понравилась, то можете поддержать проект.

Свойство indices
val vs var
Конструктор Array()
Класс Arrays
Перевернуть массив: reversedArray()
Перевернуть массив: reverse()
Сортировка элементов массива
Содержится ли элемент в массиве
Подсчитать сумму чисел в массиве
Найти среднее значение чисел в массиве
Найти наибольшее и наименьшее число в массиве
Функция intersect(): найти общие элементы двух массивов
Выбрать случайную строку из массива
shuffle(): Перемешать элементы (Kotlin 1.40)
onEach(): Операция с каждым элементом массива по очереди (Kotlin 1.40)
Удалить дубликаты
Двумерные массивы

Рассмотрим работу с массивами в Kotlin, которые являются классами.

Массив можно создать двумя способами — через конструктор Array() или через методы arrayOf(), arrayOfNulls(), emptyArray()

.

arrayOf()

Создадим массив и получим значение третьего элемента.


val myArray = arrayOf(1, 2, 3, 4, 5)
println(myArray[2])

Узнать длину массива можно при помощи свойства size.


println(myArray.size) // 5

А что случится, если мы добавим в массив строки?


val myArray = arrayOf(1, 2, 3, 4, 5, "зайчик", "вышел", "погулять")
println(myArray[5])

Ничего страшного, у нас получился массив смешанного типа. Всё работает, ничего не ломается.

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


val myArray = arrayOf<Int>(1, 2, 3, 4, 5) // только числа Integer

Существует также синонимы метода, когда уже в имени содержится подсказка: intArrayOf(), harArrayOf(), booleanArrayOf(), longArrayOf(), shortArrayOf(), byteArrayOf().

Перепишем пример.


val myArray = intArrayOf(1, 2, 3, 4, 5)

Пройтись по элементам массива и узнать значение индекса можно с помощью метода withIndex():


val numbersArray = intArrayOf(1, 2, 3, 4, 5)
for ((index, value) in numbersArray. withIndex()) {
    println("Значение индекса $index равно $value")
}

Свойство indices

У массива есть свойство indices и мы можем переписать пример по другому.


val numbers = intArrayOf(1, 2, 3, 4, 5)
for (index in numbers.indices) {
    println("Значение индекса $index равно ${numbers[index]}")
}

Свойство возвращает интервал Range, который содержит все индексы массива. Это позволяет не выйти за пределы массива и избежать ошибки ArrayIndexOutOfBoundsException.

Но у свойства есть очень интересная особенность. Взгляните на код:


val numbers = intArrayOf(1, 2, 3, 4, 5)
for(index in numbers.indices - 2) {
    println(numbers[index])
}

// 1 2 4 5

Из интервала индексов массива мы убрали третий элемент (отсчёт от 0). И теперь при выводе элементов массива мы не увидим числа 3.

Можно сложить два массива.


val numbers = intArrayOf(1, 2, 3)
val numbers3 = intArrayOf(4, 5, 6)
val foo2 = numbers3 + numbers
println(foo2[5]) // 3

arrayOfNulls()

Для создания массива с заполненными значениями null можно использовать отдельную функцию arrayOfNulls().

Создадим массив с тремя элементами.


val array = arrayOfNulls(3) // [null, null, null]
// равносильно
// arrayOf(null, null, null)

Присвоим значения пустым элементам.


var arr2 = arrayOfNulls<String>(2)
arr2.set(0, "1")
arr2.set(1, "2")

// или
arr2[0] = "1"
arr2[1] = "2"

// получить значения
println(arr2[0]) // или arr2.get(0)
println(arr2[1])

emptyArray()

Создадим пустой массив и заполним его данными.


var arr = emptyArray<String>()
arr += "1"
arr += "2"
arr += "3"
arr += "4"
arr += "5"

val vs var

Нужно уяснить разницу между var и val при работе с массивами.


// Создали новый массив
var myArray = arrayOf(1, 2, 3)

// Это совершенно новый массив
myArray = arrayOf(4, 5)

Фактически мы уничтожили первый массив и создали вместо него второй массив.

Если мы попытаем написать такой же код с использованием val, то компилятор запретит такое действие.


// Создали новый массив
val myArray = arrayOf(1, 2, 3)

// Нельзя. Компилятор не пропустит
myArray = arrayOf(4, 5)

Но при этом вы можете менять значения элементов массива, созданного через val.


val myArray = arrayOf(1, 2)
myArray[0] = 3 // меняем первый элемент массива
myArray[1] = 4 // меняем второй элемент массива

Конструктор Array()

При использовании конструктора нужно указать размер массива в первом параметре и лямбда-выражение во втором.


val myArray = Array(5, { i -> i * 2 })
println(myArray[3])

Мы задали пять элементов и каждый элемент в цикле умножаем на 2. В итоге получим массив чисел 0, 2, 4, 6, 8.

Создадим массив строк от «A» до «Z»


val letters = Array<String>(26) { i -> ('A' + i).toString() }
println(letters. joinToString(""))

Лямбда-выражение принимает индекс элемента массива и возвращает значение, которое будет помещено в массив с этим индексом. Значение вычисляется путём сложения индекса с кодом символа и преобразованием результата в строку.

Можно опустить тип массива и написать Array(26), компилятор самостоятельно определит нужный тип.

Есть отдельные классы для каждого примитивного типа — IntArray, ByteArray, CharArray и т.д.


val zeros = IntArray(3) // первый способ
val zeros = intArrayOf(0, 0, 0) // второй способ при помощи фабричного метода
println(zeros.joinToString())

Можно использовать лямбда-выражение.


val intArray = IntArray(4){i -> i + i}
println(intArray.joinToString())

Класс Arrays

Для вывода значений массива используйте класс Arrays с методом toString(), который вернёт результат в удобном и читаемом виде. Сейчас в Kotlin появилась функция contentToString(), которая является предпочтительным вариантом.


println(Arrays.toString(arr)) // старый способ
println(arr.contentToString()) // рекомендуемый способ

Перебор элементов массива

Обычный перебор через for.


val arr = arrayOf(1, 2, 3, 4, 5)

for (i in arr) {
    println("Значение элемента равно $i")
}

Можно одной строкой через forEach.


arr.forEach { i -> println("Значение элемента равно $i") }

Если нужна информация не только о значении элемента, но и его индексе, то используем forEachIndexed.


arr.forEachIndexed { index, element ->
    println("$index : $element")
}

// Результат
0 : 1
1 : 2
2 : 3
3 : 4
4 : 5

Перевернуть массив: reversedArray()

Для операции создадим дополнительную переменную для нового массива с перевёрнутыми значениями. Оригинал останется без изменений.


val numbers: IntArray = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
var reversedArray = numbers.
reversedArray() println(Arrays.toString(reversedArray))

Перевернуть массив: reverse()

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


val numbers: IntArray = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
numbers.reverse()

println(Arrays.toString(numbers))

Сортировка элементов массива

В Kotlin очень просто сортировать элементы.

Вызываем метод sort(). Мы меняем существующий массив, а не создаём новый.


val numbers: IntArray = intArrayOf(7, 5, 8, 4, 9, 6, 1, 3, 2)
numbers.sort()

// println(Arrays.toString(numbers)) // старый способ
println("Sorted array: ${numbers.contentToString()}")

Сортировать можно не весь массив, а только определённый диапазон. Указываем начало и размер диапазона. Допустим, нам нужно отсортировать только первые три элемента из предыдущего примера.


numbers.sort(0, 3)

// 5, 7, 8, 4, 9, 6, 1, 3, 2

Сортировка в обратном порядке от наибольшего значения к наименьшему.


numbers.sortDescending()

Если нужно сохранить исходный массив, то вызываем другие функции, которые создадут новый массив.


val numbers: IntArray = intArrayOf(7, 5, 8, 4, 9, 6, 1, 3, 2)
val sortedNumbers: IntArray = numbers.sortedArray() // новый сортированный массив
val descendingSortedNumber: IntArray = numbers.sortedArrayDescending() // новый сортированный массив в обратном порядке

println("Original array ${numbers.contentToString()}:Sorted array ${sortedNumbers
        .contentToString()}")
// Original array [7, 5, 8, 4, 9, 6, 1, 3, 2]:Sorted array [1, 2, 3, 4, 5, 6, 7, 8, 9]

Для сортировки объектов указываем компаратор и условие сравнения. Например, мы хотим сравнить котов по их возрастам.


val cats = arrayOf(Cat("Барсик", 8), Cat("Мурзик", 4), Cat("Васька", 9))
// массив до сортировки
cats.forEach { println(it) }

// сортируем по возрасту
cats.sortWith(Comparator { c1: Cat, c2: Cat -> c1. age - c2.age })
cats.forEach { println(it) }


data class Cat(val name: String, val age: Int)

Вместо компаратора можно использовать функцию sortBy() с указанием условия. Сравним теперь котов не по возрасту, а по их именам.


val cats = arrayOf(Cat("Барсик", 8), Cat("Мурзик", 4), Cat("Васька", 9))
cats.forEach { println(it) }
cats.sortBy { cat -> cat.name }
cats.forEach { println(it) }


data class Cat(val name: String, val age: Int)

Содержится ли элемент в массиве

Если содержится, то возвращает true.


val array = arrayOf(1, 2, 3, 4, 5)
val isContains = array.contains(9)
println(isContains) // false

Найти среднее значение чисел в массиве

Используем функцию average(). Возвращается Double.


val array = arrayOf(1, 3, 5)
println(array.average()) // 3.0

Подсчитать сумму чисел в массиве


val array = arrayOf(1, 2, 3, 4, 5)
println(array. sum()) // 15

Найти наибольшее и наименьшее число в массиве

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


val numbers: IntArray = intArrayOf(4, 9, 3, 2, 6)
var largestElement = numbers[0]

for (number in numbers){
    if(largestElement < number)
        largestElement = number
}

println("Наибольшее число в массиве: $largestElement")

Но можно не писать свой код, а вызвать готовые функции min() и max().


println(numbers.min())
println(numbers.max())

Функция intersect(): найти общие элементы двух массивов

Есть два массива с числами. Нужно сравнить их и найти у них общие числа. Поможет нам функция intersect()


val firstArray = arrayOf(1, 2, 3, 4, 5)
val secondArray = arrayOf(3, 5, 6, 7, 8)

val intersectedArray = firstArray. intersect(secondArray.toList()).toIntArray()
println(Arrays.toString(intersectedArray))

//[3, 5]

Выбрать случайную строку из массива

Имеется массив строк. Сначала вычисляем размер массива. Затем генерируем случайное число в диапазоне от 0 до (почти) 1, которое умножаем на количество элементов в массиве. После этого результат преобразуется в целое число вызовом toInt(). Получается выражение типа 0.811948208873101 * 5 = 4. В Kotlin есть свой класс Random, поэтому случайное число можно получить другим способом.


val cats = arrayOf("Барсик", "Мурзик", "Васька", "Рыжик", "Персик")
val arraySize = cats.size

// Java-style
val rand = (Math.random() * arraySize).toInt()
val name = "${cats[rand]}}"
println(name)

// Kotlin-style
val rand = Random.nextInt(arraySize)
val name = "${cats[rand]}"
println(name)

По этому принципу можно создать игру «Камень, Ножницы, Бумага».


private fun getChoice(optionsParam: Array<String>) =
    optionsParam[Random.nextInt(optionsParam.size)]
	
val options = arrayOf("Камень", "Ножницы", "Бумага")
val choice = getChoice(options)
println(choice)

shuffle(): Перемешать элементы (Kotlin 1.40)

Перемешать элементы массива в случайном порядке можно при помощи метода shuffle().


val numbers = arrayOf(1, 2, 3, 4, 5)
numbers.shuffle()
println(numbers.contentToString())

onEach(): Операция с каждым элементом массива по очереди (Kotlin 1.40)

В коллекциях мы можем пройтись по всем элементам и что-то с каждым сделать. Теперь такое возможно и с элементами массива. Пройдёмся по всем числам массива, удвоим каждое число и конвертируем в строку.


var str = ""
val numbers = arrayOf(1, 2, 3, 4, 5)
numbers.onEach {str += it * 2}
println(str)

Удалить дубликаты

Удалить дубликаты можно несколькими способами. Например, через toSet()


val myArray = arrayOf(1, 1, 2, 3, 4, 5, 5, 4, 3, 2)

println(myArray.toSet().joinToString())

Мы получим множество, которое не допускает дубликатов. Порядок элементов сохраняется. Обратно в массив можно преобразовать через toIntArray() или схожие функции.

Аналогично можно использовать toHashSet(), получив HashSet, который тоже не допускает дубликатов, но не гарантирует очерёдность элементов.

Другой вариант — toMutableSet(). Порядок сохранится.

Самый простой вариант — вызвать функцию distinct(), который вернёт новый массив без дубликатов.


val myArray = arrayOf(1, 1, 2, 3, 4, 5, 5, 4, 3, 2)
println(myArray.joinToString())

val newArray = myArray.distinct()
println(newArray.joinToString())

Двумерные массивы

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

Двумерный массив — это массив, который содержит другие массивы. Создадим двумерный массив 5х5 и заполним его нулями.


// Создаём двумерный массив
var cinema = arrayOf<Array<Int>>()

// заполняем нулями
for (i in 0..4) {
    var array = arrayOf<Int>()
    for (j in 0..4) {
        array += 0
    }
    cinema += array
}

// выводим данные массива
for (array in cinema) {
    for (value in array) {
        print("$value ")
    }
    println()
}
// Результат
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

Сейчас в кинотеатре пусто. Первый зритель берёт билет в центре зала.


// центр зала
cinema[2][2] = 1

// три места во втором ряду
for (i in 1..3) {
    cinema[3][i] = 1
}

// весь первый ряд
for (i in 0. .4) {
    cinema[4][i] = 1
}

// выводим данные массива
for (array in cinema) {
    for (value in array) {
        print("$value ")
    }
    println()
}

// Результат
0 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 1 1 1 0
1 1 1 1 1

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


var rubikCube = arrayOf<Array<Array<Int>>>()
for (i in 0..2) {
    var piece = arrayOf<Array<Int>>()
    for (j in 0..2) {
        var array = arrayOf<Int>()
        for (k in 0..2) {
            array += 0
        }
        piece += array
    }
    rubikCube += piece
}

// второй слой, третий ряд, первое место
rubikCube[1][2][0] = 1
println(Arrays.deepToString(rubikCube))

// Результат
0, 0, 0 | 0, 0, 0 | 0, 0, 0
0, 0, 0 | 0, 0, 0 | 0, 0, 0
0, 0, 0 | 1, 0, 0 | 0, 0, 0
Реклама

PhpStorm 2021.

2: дженерики, перечисления, array shapes, новые инспекции и рефакторинги

Releases

Read this post in other languages:

  • English

Вышел второй мажорный релиз PhpStorm в этом году. Ниже подробный разбор всех изменений и новых возможностей.

Скачать PhpStorm можно на сайте или через приложение Toolbox App.

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

PHP

  • Базовая поддержка дженериков для PHP
  • Перечисления из PHP 8.1
  • Однострочные array shapes
  • Новые инспекции и квик-фиксы
  • Улучшенное форматирование
  • Переработанный рефакторинг Extract Method

IDE

  • Автоматические действия при сохранении кода
  • Запуск тестов перед коммитом
  • Автоматическая очистка папок с кэшем и логами
  • Новый раздел с дополнительными настройками

Контроль версий

  • Единообразное поведение для Show Diff
  • Текстовый поиск по локальной истории
  • Подпись коммитов с помощью GPG

Code With Me

  • Демонстрация экрана

Поддержка дженериков

Дженерики остаются одной из самых желаемых фич PHP, судя по результатам опроса JetBrains Developer Ecosystem. И тем не менее на данный момент планов их реализовать на уровне языка нет.

В этом релизе мы добавили поддержку нескольких основных сценариев использования дженериков через PHPDoc-аннотации. Этот подход был популяризирован инструментами статического анализа Psalm и PHPStan.

Коллекции и template

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

Вот простейшая реализация коллекции с помощью тега @template:

Для коллекций Doctrine (doctrine/collections) в простых сценариях теперь работает автодополнение:

2021.2 не поддерживает автодополнение элементов в цикле foreach для интерфейса Collection. Этот сценарий уже реализован, но появится в одном из следующих минорных обновлений. Можно подписаться на тикет WI-61438, чтобы получать уведомления.

Поддерживаются только дженерики первого уровня
Это значит, что PhpStorm будет выводить типы и делать автодополнение только для тегов вида Base<T> или Base<T1, T2>.

А вот вложенные дженерики вида Base<Child<T>> не поддерживаются, и, скорее всего, не будут поддерживаться в будущем из-за накладных расходов на производительность.

@class-string<T>

Еще одно применение дженериков — фабрики. Они часто принимают строку с именем класса в качестве аргумента и возвращают объект.

Если вы аннотируете параметр тегом @class-string<T>, то PhpStorm предоставит вам соответствующий вывод типа:

Вообще, если вы используете оператор new для инстанцирования объекта из параметра, то можете не добавлять аннотацию @class-string — все будет работать и без нее:

Что дальше?

Планируем постепенно покрыть еще несколько важных сценариев использования дженериков. Например, вот эти:

  • WI-56034 Support the use of generics on classes
  • WI-60894 Support parameter wrapping
  • WI-61497 Generics template @implements should inherit annotations from the interface
  • WI-60895 Infer generic type from constructor

Основной тикет по дженерикам WI-47158 мы закроем, а работу продолжим уже по каждому конкретному кейсу в рамках отдельных тикетах.

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

PHP 8.1

Выход новой версии интерпретатора ожидается в конце ноября. А мы по традиции начинаем выкатывать поддержку пораньше.

Одной из самых значимых и ожидаемых фич PHP 8.1, конечно же, будут перечисления (enum).

Перечисления

Объявить перечисление можно ключевыми словами enum и case. Поскольку фича новая и синтаксис может запомниться не сразу, PhpStorm поможет вам автодополнением и подсветит ошибки синтаксиса языка.

Перечисления можно использовать в объявлениях типов (тайпхинтах) параметров, возвращаемых значений и свойств класса.

PhpStorm отследит любые ошибки, связанные с использованием типов, и подсветит их в редакторе.

Новые значения в перечислениях (cases) можно создавать с помощью специального квик-фикса: переставьте указатель туда, куда хотите добавить использование нового кейса, введите его, а затем нажмите Alt+Enter и выберите Add enum case.

Backed enums

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

Перечисления со скалярными эквивалентами для кейсов, называются backed enums.

Если вы объявили стандартное перечисление (unit enum) и затем решили сделать из него backed enum, то можно нажать Alt+Enter на подчеркнутых кейсах и добавить декларацию типа для всего перечисления.

Кстати, перечисление можно назвать Enum, потому что оно определено как контекстное ключевое слово. И поэтому, если вы использовали какие-либо пакеты с реализацией перечислений через классы, то конфликтов не возникнет — можно переключиться на PHP 8.1 и потом мигрировать.

В перечислениях можно объявлять свои методы, но нельзя объявлять конструкторы, деструкторы и магические методы (кроме __call, __callStatic, и __invoke). Также нельзя объявлять свойства и динамические свойства. Все эти нюансы PhpStorm проверит и, если что не так, подсветит ошибки.

Это все по перечислениям. Будем рады вашим идеям по возможным улучшениям!

Поддержка синтаксиса аrray shape в PHPDoc

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

В этом релизе добавили поддержку PHPDoc-синтаксиса для описания структуры массивов. Это значит, что для ключей будет автодополнение, а для значений определен их тип.

Поддержка работает только для однострочных определений. Многострочные и вложенные определения array shape не поддерживаются.

В качестве альтернативы можно использовать атрибут #[ArrayShape]. Доступен начиная с версии PhpStorm 2020.3 и среди его преимуществ: более красивая подсветка синтаксиса, поддержка многострочности, а также возможность использовать константы для описания структуры.

Улучшенное форматирование кода

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

В PhpStorm 2021.2 исправили проблемы с форматированием — теперь оно всегда работает правильно и предсказуемо!

Чтобы отформатировать код, нажмите Ctrl+Alt+L. Вы можете применить форматирование только к выделенному коду либо ко всему файлу сразу.

Все настройки стиля кода доступны в разделе Settings/Preferences | Editor | Code Style | PHP.

Некоторые функции PhpStorm было сложно использовать из-за проблем с форматированием. Например, были проблемы при разделении списков на отдельные строки / объединении их в одну строку. Теперь все будет работать как надо.

Для массивов, списков аргументов и списков параметров теперь можно нажать Alt+Enter и выбрать опцию Split comma-separated values into multiple lines:

Вы можете выбрать способ выравнивания элементов списка. Например, для параметров с тайпхинтами можно выровнять типы и имена по столбцам:

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

Новые инспекции и квик-фиксы

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

Вот некоторые из них.

Условие можно заменить оператором `?->`

Эта проверка находит выражения, которые можно заменить nullsafe-оператором из PHP 8. Нажмите Alt+Enter, чтобы выполнить замену.

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

PhpStorm подсветит константы, которые читаются через дочерний класс или интерфейс, а не через тот, где они объявлены. Заменить имя сущности можно нажав Alt+Enter.

Избыточное условие в логическом выражении

Теперь PhpStorm будет проверять все части составных условий, чтобы выявить лишние.

Вот простой пример:

А вот менее очевидный:

Инспекции

Refactoring opportunities

Добавили новую группу проверок в Settings/Preferences | Editor | Inspections.

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

Вот три примера.

Complex class should be refactored

Эта проверка находит раздутые классы полагаясь на три метрики: мера сцепленности класса (Tight Class Cohesion, TCC), сумма цикломатических сложностей для всех методов в классе (Weighted Method Count, WMC) и количество полей другого класса, к которым обращаются в данном классе (Access To Foreign Data, ATFD).

Когда PhpStorm обнаруживает раздутый класс, он предлагает извлечь методы в новый класс. Если исходный класс слишком велик, может потребоваться еще несколько итераций рефакторинга.

Complex function should be refactored

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

Если общая оценка метода низкая, то, вероятно, есть смысл разделить его на более простые, более управляемые части.

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

Method has Feature Envy of another class

Когда метод обращается к данным другого объекта чаще, чем к своим собственным данным, это признак Feature Envy.

Возможно стоит перенести метод или его часть в другой класс.

В группе Refactoring opportunities есть еще несколько инспекций, их описания можно изучить в настройках.

Extract Method доступен в PhpStorm с 2011 года и является одним из самых используемых рефакторингов.

Проблема: предположим, у вас слишком большой метод, и вы хотите разбить его на несколько маленьких. Или вы хотите устранить дублирование кода.

Решение: переместить часть кода в новый метод и заменить старый код вызовом метода.

Чтобы использовать этот рефакторинг, выделите фрагмент кода и нажмите Cmd+Alt+M / Ctrl+Alt+M.

Теперь давайте рассмотрим улучшения Extract method в PhpStorm 2021.2.

Рефакторинг для дублирующегося кода

Инспекция Duplicated code fragment позволяет находить повторяющиеся фрагменты кода.

Она была доступна и ранее, но теперь добавили квик-фикс для рефакторинга такого кода.

Инспекция подсветит только первую найденную строку. Поставьте на нее курсор, нажмите Alt+Enter и вызовите Extract Method from duplicate code.

Все повторяющиеся фрагменты будут заменены вызовом нового метода.

Обнаружение дубликатов во время рефакторинга

PhpStorm находит более мелкие дублирующиеся куски в текущем скоупе (метод, класс, файл) непосредственно во время рефакторинга. В версии 2021.2 этот процесс обнаружения стал намного умнее.

Выделите кусок кода и вызовите рефакторинг с помощью ⌘⌥M (Cmd+Alt+M / Ctrl+Alt+M). При обнаружении дубликатов, в нижней части диалога вы увидите чекбокс Review and replace duplicates.

Дубликаты будут найдены, даже если код не полностью идентичен, но схож по сути.

Новый умный селектор для фрагментов кода

Если попытаться вызвать Extract Method, не выделив ничего в редакторе, PhpStorm предложит список фрагментов кода, которые можно извлечь. Это могут быть простые выражения или целые блоки.

PhpStorm не выделит фрагмент кода, если его извлечение недопустимо или бессмысленно. В примере ниже выражение присваивания пропускается — вы можете извлечь только его правую часть.

Улучшение

Expand selection

Вы наверняка уже знаете, что можно выделять фрагменты кода с помощью горячей клавиши Expand / Shrink selection ⌥+Up / ⌥+Down (Ctrl+W / Ctrl+Shift+W). Это один из способов быстро выделить код для рефакторинга.

В PhpStorm 2021.2 немного обновили это действие — теперь вы можете выбирать блоки, исключая фигурные скобки.

Автоматическое именование для извлеченных методов

PhpStorm 2021. 2 пытается решить одну из самых сложных проблем в компьютерных науках — именование.

Когда вы используете рефакторинг Extract Method, PhpStorm предложит имя для нового метода.

Иногда это будет просто extracted, как и раньше, но в некоторых случаях PhpStorm предложит осмысленные имена на основе используемых переменных.

Если у блока кода есть комментарий, то PhpStorm учтет его при генерации имени для извлеченного метода.

IDE

Действия при сохранении кода

В PhpStorm и раньше позволял настроить действия, срабатывающие при нажатии Cmd+S / Ctrl+S. В этой версии переработали все существующие функции и собрали их в одном месте.

Теперь все опции доступны в разделе Preferences / Settings | Tools | Actions on Save. Перечисленные здесь действия можно включить в перечень действий, вызываемых при нажатии на Cmd+S / Ctrl+S.

Просто установите галочку рядом с названием нужных действий. Большинство действий будет активироваться при любом сохранении, включая автосохранение. Для действий типа “File Watcher” и “Upload to default server” вы можете выбрать один из двух вариантов активации.

Изменение иконки проекта

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

Теперь стало проще установить иконки для проектов. Для этого щелкните правой кнопкой мыши на любом проекте и выберите в контекстном меню Choose project icon.

Улучшения scratch-файлов

С помощью scratch-файлов можно делать заметки и писать черновой код вне контекста проекта. Чтобы создать новый scratch-файл, нажмите Ctrl+Shift+N.

В это релизе теперь можно выделить нужный код, нажать Alt+Enter, и выбрать Create new scratch file from selection.

Кроме того, теперь PhpStorm автоматически удаляет пустые файлы, как только вы их закрываете, чтобы помочь вам соблюдать порядок.

Настраиваемый каталог для scratch-файлов

Вы можете настроить папку для хранения scratch-файлов. По умолчанию это каталог конфигурации IDE, но можно указать расшаренную по сети папку и использовать одни и те же scratch-файлы на разных компьютерах.

Для этого необходимо указать опцию командной строки:

-Didea.scratch.path/scratches=<path>

Улучшения в диалоге настроек

В настройках появился новый раздел Advanced Settings. Он содержит некоторые дополнительные параметры конфигурации. Большинство из них были перенесены из Registry.

Там же вы найдете некоторые новые параметры, например, возможность установить отступ слева в режиме Distraction-free mode.

Планируем переносить редко используемые настройки в этот раздел, чтобы сделать основные настройки чище.

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

Автоматическая очистка папок с кэшем и логами

Раньше файлы логов и кэша PhpStorm со временем начинали занимать значительное количество места на жестком диске.

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

Можно запустить очистку вручную из главного меню: Help | Delete Leftover IDE Directories….

Быстрый доступ к режиму Power Save

Не секрет, что иногда загрузка CPU при использовании PhpStorm может быть довольно высокой. В основном это происходит во время индексирования. Мы специально используем много ресурсов при индексировании, чтобы максимально ускорить его.

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

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

Если вы работаете без статус-бара, включить энергосберегающий режим можно в меню File | Power Save Mode.

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

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

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

Контроль версий

Единообразное поведение для

Show Diff

Теперь вы можете просмотреть дифф изменений файла прямо во вкладке редактора.

Если вам удобнее отслеживать изменения в отдельном окне, вы можете вынести нужный файл в отдельное окно. При этом PhpStorm запомнит, что вы это сделали, и будет открывать последующие диффы в отдельном окне.

GPG-подписи для Git

Теперь можно подписывать Git-коммиты с помощью GPG. Настроить это можно в разделе Preferences/Settings | Version Control | Git.

Текстовый поиск по версиям Local History

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

Чтобы просмотреть локальную историю файла, щелкните правой кнопкой мыши по названию файла и выберите Local History | Show History.

Теперь в этом окне есть поле поиска, с помощью которого можно найти нужное изменение.

Запуск тестов перед коммитом

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

В PhpStorm 2021.2 появилась еще одна опция — Run Tests.

Нажмите на значок шестеренки в Commit view, выберите опцию Run Tests и выберите нужную Run-конфигурацию из списка. PhpStorm запустит тесты и уведомит вас, если что-то пойдет не так.

Также теперь вы можете настроить параметры для Analyze code и Cleanup. Для этого нажмите кнопку Choose profile рядом с ними.

Вы сможете следить за ходом и результатами проверок в области Commit — PhpStorm не будет отвлекать вас дополнительными модальными окнами.

Code With Me

Code With Me — это инструмент для парного программирования, который поставляется в PhpStorm из коробки. В этом выпуске он был улучшен множеством дополнительных функций и обновлений. Обо всех изменениях можно прочитать в отдельном посте про Code With Me 2021.2.

Демонстрация экрана (Screen Sharing)

Одна из самых желанных фич для Code With Me теперь доступна в 2021.2 — теперь вы можете расшарить окно PhpStorm или весь экран.

Инструменты для работы с БД

PhpStorm включает встроенные возможности DataGrip. Подробнее обо новых возможностях DataGrip читайте в обзоре релиза DataGrip 2021.2 от наших коллег.

Веб-разработка

И как всегда, в PhpStorm вошли все обновления из WebStorm 2021. 2.


А вот видеоролик (на английском) с главными фичами нового релиза:


На этом пока все. Будем рады вашей обратной связи — вопросам, пожеланиям, баг-репортам и просто мыслям. Оставляйте их в комментариях ниже.

Ваша команда PhpStorm
The Drive to Develop

Оригинал статьи опубликован на habr.com

ArrayShape enums Extract Method Generics Inspections PHP 8.1 Refactorings

Prev post PhpStorm 2021.1: превью PHP- и HTML-файлов, 20+ новых инспекций и парное программирование с Code With MeВстречайте PhpStorm 2021.3! Next post

Подписаться на блог

Subscribe form

By submitting this form, I agree to the JetBrains Privacy Policy Notification icon

Отправляя эту форму, я разрешаю JetBrains s. r.o. («JetBrains») использовать мое имя, электронный адрес и геоданные для отправки мне новостных рассылок, включая коммерческие предложения, а также разрешаю JetBrains осуществлять обработку моих персональных данных с указанной целью. Я соглашаюсь с тем, что такая обработка может выполняться с использованием сторонних сервисов в соответствии с Политикой конфиденциальности JetBrains. Я могу в любой момент отозвать согласие в своем профиле. Кроме того, ссылка для отмены подписки содержится в каждом электронном письме.

Усовершенствованная деструктуризация массивов в PHP

В своей повседневной работе я пишу на нескольких языках программирования. Большую часть времени я трачу на написание PHP, но мне очень нравится писать и на других языках, таких как Go и Javascript!

Одна из вещей, которые мне больше всего нравятся в JavaScript, а в некоторой степени и в PHP, это то, насколько гибкими и выразительными они МОГУТ быть (но не всегда). Я также считаю, что JavaScript, PHP и Python обладают рядом особенностей, которые делают их подходящими для использования на переднем крае Интернета. Большинство из этих функций связаны со свободной и динамической типизацией. Лично мне нравится, как свободно вы можете преобразовывать данные, не слишком беспокоясь о структуре.

Наиболее универсальным типом PHP для меня является массив . Массив можно использовать как множество различных классических типов данных. Вы можете использовать их как List , Set (хотя это требует определенной обработки), HashMap , и это лишь некоторые из них.

Суперспособности массива PHP 7.1!

В PHP 7.1 тип массива стал еще мощнее! Был принят (и реализован) RFC для обеспечения синтаксиса квадратных скобок для назначения деструктурирования массива.

Это изменение языка упростило извлечение значений из массива. Например, рассмотрим следующий массив опций:

  true, 'compression' => 'gzip'];
 

Если бы мы хотели присвоить параметры переменным, мы могли бы использовать что-то вроде этого:

 $enabled = $options['enabled'];
$compression = $options['сжатие'];
 

Это не так уж плохо, но, используя возможности этого нового RFC, мы могли бы определить это чуть более выразительно. Возможно, вы знаете такое выражение, если использовали его форму в ES6.

 let options = {включено: правда, сжатие: 'gzip'};
let {включено, сжатие} = параметры;
 

В этом примере ключи с включенным и сжатием извлекаются в переменные в языковом контракте, который можно рассматривать как инверсию исходного назначения options . В этом примере имена переменных являются неявными. В полной форме пример немного более подробный.

 пусть {включено: включено, сжатие: сжатие} = параметры;
 

Расширение также позволяет переименовывать и может быть смешано с более короткой версией:

 пусть {включено, сжатие: алгоритм} = параметры;
 

Начиная с версии 7.1 мы можем использовать такой же тип синтаксиса и в PHP!

 $options = ['enabled' => true, 'compression' => 'gzip'];
['enabled' => $enabled, 'compression' => $compression] = $options;
 

В этом примере значение ключа enable назначается ключу $enabled 9. 0008, и то же самое касается ключа сжатия .


Помимо деструктуризации ассоциативного массива, возможна деструктуризация списка. Этот тип деструктурирования особенно полезен, когда вы используете массивы, как Tuple . Кортеж — это конечный упорядоченный список, содержащий фиксированное количество элементов.

 [$a, $b] = [1, 2];
// $а теперь 1
// и $b равно 2
 

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

 $а = 1;
$б = 2;
[$a, $b] = [$b, $a];
// $a теперь равно 2
// и $b равно 1
 

Вложенная деструктуризация

Во многих приложениях PHP приложение отвечает за получение большого набора вложенных данных. В этом случае может пригодиться вложенная форма деструктуризации массива.

 $options = ['enabled' => true, 'compression' => ['algo' => 'gzip']];
[
    'включено' => $ включено,
    'сжатие' => [
        'алго' => $компрессионалго
    ]
] = $опции;
// $enabled имеет значение TRUE
// $compressionAlgo — это "gzip"
 

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