Статические методы — Java: Введение в ООП
Одна из базовых конструкций в программировании — функция. С помощью функций мы выделяем повторяющиеся блоки кода и вводим новые операции, которые не заложены в язык. В большинстве языков функции создаются просто, для этого достаточно знать синтаксис определения. Ниже пример на JavaScript, который будет понятен даже без знания языка:
// Функция возвращает строку // Типы определять не надо, js динамический язык function greeting() return 'just a string'; > // Вызов можно делать в любом месте // Даже просто в файле, вне других функций greeting(); // 'Just a string'
С Java все немного сложнее. Она не позволяет создавать обычные функции. Java требует наличия класса для определения функций, которые, в этом случае, называются методами. Для сравнения перепишем пример с JavaScript на Java:
// Имя класса можно выбирать произвольно class App // public - дает возможность вызывать метод снаружи класса // static - пока просто нужно, позже разберемся // String - возвращаемый тип public static String greeting() return "just a string"; > > // Любой код в Java выполняется только внутри классов // Поэтому вызов метода greeting() тоже будет где-то внутри // Где-то в другом месте программы class SomeName public static void someMethod() // Для вызова статического метода нужно // указывать имя класса App.greeting(); > >
Если вызов метода идет из того же класса, где он определен, то указывать имя класса перед ним не нужно:
class App public static String greeting() return "just a string"; > public static void main(String[] args) // Тот же класс, поэтому имя не обязательно greeting(); // Хотя, чисто технически, его можно поставить // App.greeting(); > >
Основная разница между функцией greeting() на JavaScript и методом greeting() на Java заключается в наличии класса в определении метода и его же указание при вызове из других классов. Возникает вопрос, зачем нужен класс когда можно просто создавать функции? Вообще так делать было не обязательно, но создатели Java решили по-другому. Например, в Kotlin, который тоже работает на виртуальной машине Java, нет такого ограничения, там можно создавать обычные функции.
Несмотря на наличие класса и смены названия на «метод», по сути, мы получили обычную функцию, которая определена в классе и вызывается через класс. Достигается это за счет ключевого слова static . Без него методы работают совсем по-другому, о чем мы поговорим в следующих уроках. Методы, определенные как static , называют статическими.
Еще немного примеров статических методов из реальной жизни:
// Встроенные в Java // возвращает модуль числа var y = Math.abs(-30); // находит минимальное число var m = Math.min(10, 11); // Из библиотеки org.apache.commons.lang3 // возвращает случайное число var x = RandomUtils.nextInt(1, 1000);
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
Что такое статические методы
СТАТИСТИЧЕСКИЕ МЕТОДЫ
- Описание
- Алфавитный указатель
- Арабская философия
- Индийская философия
- Китайская философия
- Русская философия
- Этика
- Авторы
- Приложения
СТАТИСТИЧЕСКИЕ МЕТОДЫ – научные методы описания и изучения массовых явлений, допускающих количественное (численное) выражение. Слово «статистика» (от итал. stato – государство) имеет общий корень со словом «государство». Первоначально оно относилось к науке управления и означало сбор данных о некоторых параметрах жизнедеятельности государства. Со временем статистика стала охватывать сбор, обработку и анализ данных о массовых явлениях вообще; ныне статистические методы охватывают собою практически все области знаний и жизнедеятельности общества.
Статистические методы включают в себя и экспериментальное, и теоретическое начала. Статистика исходит прежде всего из опыта; недаром ее зачастую определяют как науку об общих способах обработки результатов эксперимента. Обработка массовых опытных данных представляет самостоятельную задачу. Иногда простая регистрация некоторых рядов наблюдений приводит к тому или иному значимому выводу. Так, если в некоторой стране из года в год растет объем валового внутреннего продукта, то это говорит об ее устойчивом развитии. Однако в большинстве случаев для обработки опытного статистическою материала используются математические модели исследуемого явления, основу которых составляют идеи и методы теории вероятностей.
Теория вероятностей есть наука о массовых случайных явлениях. Массовость означает, что исследуются огромные количества однородных явлений (объектов, процессов). Случайность же означает, что значение рассматриваемого параметра отдельного явления (объекта) в своей основе не зависит и не определяется значениями этого параметра у других явлений, входящих в ту же совокупность. Основной характеристикой массового случайного явления является распределение вероятностей. Теорию вероятностей можно определить как науку о вероятностных распределениях – их свойствах, видах, законах взаимосвязей, распределении величин, характеризующих исследуемый объект, и законах изменения распределений во времени. Так, говорят о распределении молекул газа по скоростям, о распределениях доходов граждан в некотором обществе и т.д.
Эмпирически задаваемые распределения соотносятся с т.н. генеральной совокупностью, т.е. с наиболее полным теоретическим описанием распределений соответствующих массовых явлений. При этом во многих случаях бывает нецелесообразно «перебирать» все элементы рассматриваемых совокупностей либо в силу чрезвычайно большого их числа, либо в силу того, что при наличии некоторого числа «перебранных» элементов учет новых не внесет существенных изменений в общие результаты. Для этих случаев разработан специальный выборочный метод исследования общих свойств статистических систем на основе изучения лишь части соответствующих элементов, взятых на выборку. Так, при оценке политических симпатий граждан некоторого региона или страны перед предстоящими выборами невозможно проводить сплошной опрос граждан. В этих случаях и прибегают к выборочному методу. Чтобы выборочное распределение достаточно надежно характеризовало исследуемую систему, оно должно удовлетворять специальным условиям репрезентативности. Репрезентативность требует случайного выбора элементов и учета макроструктуры всего массового явления.
Распределения представляют наиболее общую характеристику массовых случайных явлений. Задание исходного распределения нередко предполагает построение математической модели соответствующих областей действительности. Построение и анализ таких моделей и составляет основную направленность статистических методов. Построенная математическая модель, в свою очередь, указывает, какие переменные следует измерять и какие из них имеют основное значение. Но главное в построении математической модели состоит в объяснении исследуемых явлений и процессов. Если модель достаточно полна, то она описывает зависимости между основными параметрами этих явлений.
Статистические методы в естествознании породили многие научные теории, привели к разработке важнейших фундаментальных направлений исследования – классической статистической физики, генетики, квантовой теории, теории цепных химических реакций и др. Следует, однако, отметить, что во многих случаях исходные вероятностные распределения задаются не путем непосредственной обработки массового материала. Вероятностная гипотеза чаще всего вводится гипотетически, косвенно, на основе теоретических предпосылок. Так, в учение о газах предположение о существовании вероятностных распределений было введено как гипотеза, на основе допущений о «молекулярном беспорядке». Возможность подобного задания вероятностных распределений и проверки их справедливости обусловлена характером и природой самих распределений, математическое выражение которых обладает самостоятельными характеристиками, достаточно независимыми от конкретных значений элементов.
Особые сложности возникают при применении статистических методов в изучении социальных явлений. Анализ общих направлений социальных процессов и внутренних механизмов, вызывающих конкретные статистические результаты, необычайно трудоемок. Так, благосостояние людей характеризуется весьма многими параметрами и соответствующими распределениями – уровнем доходов, участием в общественно-полезном труде, уровнем образования и здравоохранения и др. показателями жизнедеятельности человека. Выявление взаимосвязи этих распределений и тенденций их изменения требует решения многих сложных задач. Состояние общества можно определить через такие параметры, как внутренний валовый продукт, потребление энергии на душу населения, расслоение общества по доходам и т.п. Вместе с тем общество представляет собой необычайно сложную систему, а познание сложных систем основывается на разработке многих моделей, выражающих различные аспекты их структуры и функционирования. Соответственно, для более полной характеристики состояния общества требуется оперировать весьма многими параметрами и их распределениями. Так, говорят об экономической, производственной, сельскохозяйственной, социальной и многих других статистиках. Для объединения данных этих статистик в единую целостную картину необходимо выявление субординации, иерархии параметров, характеризующих состояние общества.
Статические свойства и методы
Мы также можем присвоить метод самому классу. Такие методы называются статическими.
В объявление класса они добавляются с помощью ключевого слова static , например:
class User < static staticMethod() < alert(this === User); >> User.staticMethod(); // true
Это фактически то же самое, что присвоить метод напрямую как свойство функции:
class User < >User.staticMethod = function() < alert(this === User); >;
Значением this при вызове User.staticMethod() является сам конструктор класса User (правило «объект до точки»).
Обычно статические методы используются для реализации функций, которые будут принадлежать классу в целом, но не какому-либо его конкретному объекту.
Звучит не очень понятно? Сейчас все встанет на свои места.
Например, есть объекты статей Article , и нужна функция для их сравнения.
Естественное решение – сделать для этого статический метод Article.compare :
class Article < constructor(title, date) < this.title = title; this.date = date; >static compare(articleA, articleB) < return articleA.date - articleB.date; >> // использование let articles = [ new Article("HTML", new Date(2019, 1, 1)), new Article("CSS", new Date(2019, 0, 1)), new Article("JavaScript", new Date(2019, 11, 1)) ]; articles.sort(Article.compare); alert( articles[0].title ); // CSS
Здесь метод Article.compare стоит «над» статьями, как средство для их сравнения. Это метод не отдельной статьи, а всего класса.
Другим примером может быть так называемый «фабричный» метод.
Скажем, нам нужно несколько способов создания статьи:
- Создание через заданные параметры ( title , date и т. д.).
- Создание пустой статьи с сегодняшней датой.
- …или как-то ещё.
Первый способ может быть реализован через конструктор. А для второго можно использовать статический метод класса.
Такой как Article.createTodays() в следующем примере:
class Article < constructor(title, date) < this.title = title; this.date = date; >static createTodays() < // помним, что this = Article return new this("Сегодняшний дайджест", new Date()); >> let article = Article.createTodays(); alert( article.title ); // Сегодняшний дайджест
Теперь каждый раз, когда нам нужно создать сегодняшний дайджест, нужно вызывать Article.createTodays() . Ещё раз, это не метод одной статьи, а метод всего класса.
Статические методы также используются в классах, относящихся к базам данных, для поиска/сохранения/удаления вхождений в базу данных, например:
// предположим, что Article - это специальный класс для управления статьями // статический метод для удаления статьи по id: Article.remove();
Статические методы недоступны для отдельных объектов
Статические методы могут вызываться для классов, но не для отдельных объектов.
Например. такой код не будет работать:
// . article.createTodays(); /// Error: article.createTodays is not a function
Статические свойства
Новая возможность
Эта возможность была добавлена в язык недавно. Примеры работают в последнем Chrome.
Статические свойства также возможны, они выглядят как свойства класса, но с static в начале:
class Article < static publisher = "Илья Кантор"; >alert( Article.publisher ); // Илья Кантор
Это то же самое, что и прямое присваивание Article :
Article.publisher = "Илья Кантор";
Наследование статических свойств и методов
Статические свойства и методы наследуются.
Например, метод Animal.compare в коде ниже наследуется и доступен как Rabbit.compare :
class Animal < constructor(name, speed) < this.speed = speed; this.name = name; >run(speed = 0) < this.speed += speed; alert(`$бежит со скоростью $.`); > static compare(animalA, animalB) < return animalA.speed - animalB.speed; >> // Наследует от Animal class Rabbit extends Animal < hide() < alert(`$прячется!`); > > let rabbits = [ new Rabbit("Белый кролик", 10), new Rabbit("Чёрный кролик", 5) ]; rabbits.sort(Rabbit.compare); rabbits[0].run(); // Чёрный кролик бежит со скоростью 5.
Мы можем вызвать Rabbit.compare , при этом будет вызван унаследованный Animal.compare .
Как это работает? Снова с использованием прототипов. Как вы уже могли предположить, extends даёт Rabbit ссылку [[Prototype]] на Animal .
Так что Rabbit extends Animal создаёт две ссылки на прототип:
- Функция Rabbit прототипно наследует от функции Animal .
- Rabbit.prototype прототипно наследует от Animal.prototype .
В результате наследование работает как для обычных, так и для статических методов.
Давайте это проверим кодом:
class Animal <> class Rabbit extends Animal <> // для статики alert(Rabbit.__proto__ === Animal); // true // для обычных методов alert(Rabbit.prototype.__proto__ === Animal.prototype); // true
Итого
Статические методы используются для функциональности, принадлежат классу «в целом», а не относятся к конкретному объекту класса.
Например, метод для сравнения двух статей Article.compare(article1, article2) или фабричный метод Article.createTodays() .
В объявлении класса они помечаются ключевым словом static .
Статические свойства используются в тех случаях, когда мы хотели бы сохранить данные на уровне класса, а не какого-то одного объекта.
class MyClass < static property = . ; static method() < . >>
Технически, статическое объявление – это то же самое, что и присвоение классу:
MyClass.property = . MyClass.method = .
Статические свойства и методы наследуются.
Для class B extends A прототип класса B указывает на A : B.[[Prototype]] = A . Таким образом, если поле не найдено в B , поиск продолжается в A .
Задачи
Класс расширяет объект?
важность: 3
Как мы уже знаем, все объекты наследуют от Object.prototype и имеют доступ к «общим» методам объекта, например hasOwnProperty .
class Rabbit < constructor(name) < this.name = name; >> let rabbit = new Rabbit("Rab"); // метод hasOwnProperty от Object.prototype alert( rabbit.hasOwnProperty('name') ); // true
Но что если мы явно напишем «class Rabbit extends Object» – тогда результат будет отличаться от обычного «class Rabbit» ?
Ниже пример кода с таким наследованием (почему он не работает? исправьте его):
class Rabbit extends Object < constructor(name) < this.name = name; >> let rabbit = new Rabbit("Кроль"); alert( rabbit.hasOwnProperty('name') ); // Ошибка
Сперва давайте разберёмся, почему код не работает.
Причина становится очевидна, если мы попытаемся запустить его. Унаследованный конструктор класса должен вызывать super() . В противном случае «this» будет не определён.
class Rabbit extends Object < constructor(name) < super(); // надо вызвать конструктор родителя, когда наследуемся this.name = name; >> let rabbit = new Rabbit("Кроль"); alert( rabbit.hasOwnProperty('name') ); // true
Но это ещё не все.
Даже после исправления есть важное различие между «class Rabbit extends Object» и class Rabbit .
Как мы знаем, синтаксис «extends» устанавливает 2 прототипа:
- Между «prototype» функций-конструкторов (для методов)
- Между самими функциями-конструкторами (для статических методов).
В случае с class Rabbit extends Object это значит:
class Rabbit extends Object <> alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true alert( Rabbit.__proto__ === Object ); // (2) true
Таким образом, Rabbit предоставляет доступ к статическим методам Object через Rabbit , например:
class Rabbit extends Object <> // обычно мы вызываем Object.getOwnPropertyNames alert( Rabbit.getOwnPropertyNames() ); // a,b
Но если явно не наследуем от объекта, то для Rabbit.__proto__ не установлено значение Object .
class Rabbit <> alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true alert( Rabbit.__proto__ === Object ); // (2) false (!) alert( Rabbit.__proto__ === Function.prototype ); // как у каждой функции по умолчанию // ошибка - нет такой функции у Rabbit alert( Rabbit.getOwnPropertyNames() ); // Ошибка
Таким образом, в этом случае у Rabbit нет доступа к статическим методам Object .
Кстати, у Function.prototype также есть «общие» методы, такие как call , bind и т. д. Они в конечном итоге доступны в обоих случаях, потому что для встроенного конструктора Object Object.__proto__ === Function.prototype .
Пример на картинке:
Короче говоря, есть два отличия:
class Rabbit | class Rabbit extends Object |
---|---|
– | необходимо вызвать super() в конструкторе |
Rabbit.__proto__ === Function.prototype | Rabbit.__proto__ === Object |
Когда использовать статические методы
В обсуждениях к посту (перевод) о именованных конструкторах прозвучало мнение, что статические методы плохи и их вообще не стоит использовать. На мой взгляд, это слишком большое обобщение.
Статические методы по сути своей просто способ организации глобальных функций в пространства имен. Использование пространств имен, я думаю, вы согласитесь — хороший тон. Что касается глобальных функций — мы используем их всегда, встроенные функции PHP составляют основу нашего кода.
Основная проблема здесь — отсутствие совместно используемого глобального состояния. Вот пример из прошлого поста:
В данном примере возвращаемый результат свободен от побочных эффектов и вполне предсказуем, т.к. зависит только от аргументов, подаваемых на вход. Каждый раз при вызове метода вам будет возвращен идентичный результат (объект Time со значением 11:45), вне зависимости от состояния системы, контекста или чего-либо еще.
Другой пример:
И снова — результат предсказуем, Calculator::sum(1, 2); предоставляет нам сервис, не имеющий состояния, и не зависящий ни от чего, кроме аргументов. Более того, эта реализация не может быть полиморфной или иметь различные имплементации, т.к. любой результат кроме 3 будет ошибкой. Да, вы можете изменить внутреннюю реализацию метода, улучшив алгоритм сложения чисел, но это не должно никак отражаться на результате его использования.
Возьмем обратный пример, на этот раз с состоянием:
Пример элементарный, но в более сложных ситуациях это может быть не столь доходчиво. Представьте, что два разработчика используют в своем коде счетчики. Когда они тестируют свое решение изолированно — нет никаких проблем. Но после интеграции их решений счетчик начинает работать не так, как ожидалось, потому что используется глобальное состояние, вместо того, чтобы воспользоваться отдельным экземпляром счетчика.
Абстракция
Возможно, вы все еще чувствуете неприятие против кода, вроде Calculator::sum($x, $y) , т.к. мы не можем сымитировать или расширить его. Но не стоит забывать, что это довольно низкий уровень абстракции. Вы также не сможете сымитировать и расширить оператор + в PHP, но я не думаю, что вы когда-либо чувствовали потребность в этом. Если вам нужен более высокий уровень абстракции, то композиция — ваш верный спутник. Но хочу заметить, между Calculator::sum($x, $y) и + есть довольно интересное различие, первый может вот так, а второй нет:
Это все может показаться избыточным, но не стоит забывать про функции и статические методы, ведь они могут быть очень полезны при правильном их применении.
Часть 1: Как использовать именованные конструкторы в PHP
- Веб-разработка
- PHP
- Программирование