Должны быть статические методы Java по умолчанию?


скажем, вы пишете метод foo() в классе A. foo никогда не получает доступ ни к одному из состояний A. Вы больше ничего не знаете о том, что делает фу, или как он себя ведет. Он мог сделать все, что угодно.

должен ли foo всегда быть статичным, независимо от каких-либо других соображений? Почему бы и нет?

Кажется, что мои классы всегда накапливают много частных вспомогательных методов, поскольку я разбиваю задачи и применяю принцип только-записи-один раз. Большинство из них не зависят от состояния объекта, но никогда не быть полезным вне собственных методов класса. Должны ли они быть статическими по умолчанию? Это неправильно, чтобы в конечном итоге с большим количеством внутренних статических методов?

23 63

23 ответа:

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

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

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

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


1. Главное преимущество: код более выразителен.

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

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

2. Еще одно преимущество: код может быть проще рассуждать.

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

3. Оптимизация преимущества

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

4. Различия на уровне байт-кода

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

чтобы облегчить этот раздел поймите, давайте воспользуемся примером:

public class App {
    public static void main(String[] args) {
        WithoutStaticMethods without = new WithoutStaticMethods();
        without.setValue(1);
        without.calculate();

        WithStaticMethods with = new WithStaticMethods();
        with.setValue(1);
        with.calculate();
    }
}

class WithoutStaticMethods {

    private int value;

    private int helper(int a, int b) {
        return a * b + 1;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public int calculate() {
        return helper(value, 2 * value);
    }
}

class WithStaticMethods {

    private int value;

    private static int helper(int a, int b) {
        return a * b + 1;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public int calculate() {
        return helper(value, 2 * value);
    }
}

линии, которые нас интересуют, - это вызовы helper(...) на классы WithoutStaticMethods и WithStaticMethods.

без статических методов

в первом случае, без статических методов, при вызове вспомогательного метода JVM необходимо нажать ссылку на экземпляр, чтобы передать его в invokespecial. Взгляните на код calculate() способ:

 0 aload_0
 1 aload_0
 2 getfield #2 <app/WithoutStaticMethods.value>
 5 iconst_2
 6 aload_0
 7 getfield #2 <app/WithoutStaticMethods.value>
10 imul
11 invokespecial #3 <app/WithoutStaticMethods.helper>
14 ireturn

инструкция на 0 (или 1),aload_0 загрузит ссылка на экземпляр в стеке, и он будет использоваться позже invokespecial. Эта инструкция поместит это значение в качестве первого параметра helper(...) функция, и она никогда не используется, как мы видим здесь:

0 iload_1
1 iload_2
2 imul
3 iconst_1
4 iadd
5 ireturn

посмотреть нет iload_0? Он был загружен без необходимости.

со статическими методами

теперь, если вы объявите вспомогательный метод, статический, тогда calculate() метод будет выглядеть так:

 0 aload_0
 1 getfield #2 <app/WithStaticMethods.value>
 4 iconst_2
 5 aload_0
 6 getfield #2 <app/WithStaticMethods.value>
 9 imul
10 invokestatic #3 <app/WithStaticMethods.helper>
13 ireturn

различия являются:

  • на aload_0 - инструкции
  • вспомогательный метод теперь вызывается с помощью invokestatic

ну, код вспомогательной функции также немного отличается: нет this в качестве первого параметра, поэтому параметры фактически находятся в позициях 0 и 1, Как мы видим здесь:

0 iload_0
1 iload_1
2 imul
3 iconst_1
4 iadd
5 ireturn

вывод

С точки зрения дизайна кода имеет гораздо больше смысла объявлять вспомогательный метод статические: код говорит сам за себя, он содержит больше полезной информации. Он утверждает, что ему не нужно состояние экземпляра для работы.

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

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

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

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

public class MathUtils
{
  public static float squareRoot(float x)
  {
    ... calculate square root of parameter x ...
    return root;
  }
}

конечно, можно сделать "более Оопы" версия, которая выглядит так:

public class MathUtils
{
  private float x;
  public MathUtils(float x)
  {
    this.x=x;
  }
  public float squareRoot()
  {
    ... calculate square root of this.x ...
    return root;
  }
}

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

(и да, теперь у меня есть функция квадратного корня в стандартном математическом классе. Я просто использовал это как удобный пример.)

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

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

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

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

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

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

нет. Никогда. Статические методы должны быть исключением. OO-это все о наличии объектов с поведением, которое вращается вокруг состояния объекта. Imho, в идеале, не должно быть никаких (или очень мало) статических методов, потому что все, что не связано с состоянием объекта, может (и чтобы избежать приведения концепции объекта к абсурду, должно) быть помещено в простую старую функцию на уровне модуля. Возможное исключение для фабрик из-за сложности.fromCartesian (чтобы взять пример Википедии) читает так что ж.

конечно, это (изменение: уровня модуля функции) не является возможным в одной парадигме ОО язык (редактировать: как Java) - вот почему я такой преданный сторонник мульти-парадигмы языка дизайна, кстати. Но даже в языке исключительно OO большинство методов будет вращаться вокруг состояния объекта и, следовательно, быть нестатичным. То есть, если ваш дизайн не имеет ничего общего с ОО - но в этом случае, вы используете неправильный язык.

Я обычно

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

a) я пишу некоторый код в методе члена, выясните, что я, вероятно, могу повторно использовать некоторые из этого кода и

извлечение в нестатический метод

b) теперь я посмотрю, нужен ли этому методу доступ к состоянию или если я могу вписать его потребности в один или два параметра и оператор return. Если последнее имеет место:

сделать метод (частный) статика

c) если я найду, что я могу использовать этот код в других классах того же пакета я буду

сделать метод открытым и переместить метод в вспомогательный класс пакета с видимостью по умолчанию

например, в упаковке com.mycompany.foo.bar.phleeem Я бы создал класс PhleeemHelper или PhleeemUtils с видимостью по умолчанию.

d) Если я тогда понимаю, что мне нужна эта функциональность во всем моем приложении, я

переместить вспомогательный класс В a выделенный пакет утилит

например com.mycompany.foo.utils.PhleeemUtils

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

Если вы не передадите ссылку на объект, a static метод в классе обеспечивает, что сам метод не может мутировать объект, потому что ему не хватает доступа к this. В связи с этим,static модификатор предоставляет информацию программисту о намерении метода, что является побочным эффектом бесплатно.

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

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

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

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

Java объединяет понятия модуля, пространства имен, adt и класса, поэтому утверждать, что некоторая классовая OO-чистота должна препятствовать использованию класса java в качестве модуля, пространства имен или adt, смешно.

да методы должны быть статическими. Чисто внутренние методы поддержки должны быть частными; вспомогательные методы защищены; и служебные функции должны быть общедоступными. Кроме того, существует огромная разница между статическим полем, статической константой и общедоступным статическим методом. Этот во-первых, это просто еще одно слово для "глобальной переменной"; и почти всегда следует избегать, даже посредничество с помощью методов доступа едва ограничивает ущерб. Второй-это рассмотрение класса java как пространства имен для символьной константы, вполне приемлемой. В-третьих, класс java рассматривается как модуль для функции, как правило, побочных эффектов следует избегать или, при необходимости, ограничиваться любыми параметрами, передаваемыми функции. Использование статики поможет гарантировать, что вы случайно не сломаете это путем доступа к членам объекта.

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

в конечном счете java имеет очень слабые конструкции области видимости, объединяя многочисленные концепции под одним и тем же синтаксис' class 'и' interface'. Вы должны не столько "по умолчанию" для статики, сколько свободно использовать возможности java для предоставления пространств имен, ADT, модулей и т. д., Как и когда вы чувствуете необходимость в них.

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

в любом случае, все java.утиль.Массивы класса являются статическими. Числовые классы Integer, Boolean, String имеют статические методы. Много статических методов. Все статические методы в эти классы либо преобразуются в соответствующие экземпляры классов, либо из них.

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

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

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

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

поэтому, разработайте программную модель, хотите ли вы MVP, инъекцию, управляемую аспектом, уровень статического избегания / сходства и т. д. И знаете, почему вы их хотите - не потому, что какой-то теоретический орех сказал вам, что ваша практика программирования нарушит принципы oo. Помните, что если вы работаете в отрасли, то это всегда качество и рентабельность, а не теоретическая чистота.

наконец, что такое объектно-ориентированный? Объектная ориентация и нормализация данных являются стратегиями для создания ортогональной информационной перспективы. Например, в прежние времена инструкции IBM были написаны так, чтобы быть очень ортогональный. То есть, если часть информации записана где-то на странице в этих тысячах руководств, они избегают повторения этой информации. Это плохо, потому что вы будете читать обучение выполнению определенной задачи и часто сталкиваетесь с концепциями, упомянутыми в других руководствах, и вам нужно будет ознакомиться с "моделью данных" руководств, чтобы охотиться за этими соединительными частями информации thro тысячи руководств.

по той же причине OS / 2 не смогла конкурировать с Microsoft потому что концепция ортогональности IBM была чисто машинной и основанной на данных, и IBM так гордо заявляла о своей истинной объектной ориентации против ложной объектной ориентации Microsoft, потворствующей человеческой перспективе. Они забыли, что у нас, людей, есть свои собственные различные ортогональные перспективы информации, которые не соответствуют данным и машинной ортогональности или даже друг другу.

Если вы знакомы с топологией деревьев, вы бы поняли, что можно выбрать любой листовой узел и сделать его корнем. Или даже любой узел, если вы не возражаете иметь многоствольное дерево. Каждый думает, что его / ее узел является корнем, когда на самом деле любой может быть корнем. Если вы считаете, что ваша перспектива объектной ориентации является каноном, подумайте еще раз. Более важным является минимизация количества узлов, которые принимаются в качестве корней-кандидатов.

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

Если он ничего не делает с объектами этого класса, но на самом деле принадлежит к этому классу (я бы рассмотрел его перемещение в другое место), да, он должен быть статическим.

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

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

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

Это зависит от I. G. java.ленг.Математика не имеет метода, который не является статическим. (Вы можете сделать статический импорт, чтобы написать cos () вместо математики.потому что()) Это не должно быть злоупотреблено, но как некоторый код, который предназначен для вызова в качестве утилиты, это было бы приемлемо. И. Г нить.currentThread()

статический метод используется для идентификации метода (или переменной в этом отношении), который не имеет отношения к объектам, созданным из этого класса, но сам класс. Например, вам нужна переменная для подсчета количества созданных объектов. Вы бы поставили что-то вроде: 'private static int instances = 0;' а затем поместили что-то в конструктор для этого класса, который увеличивает "экземпляры", чтобы вы могли его подсчитать.

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

Джошуа блох в "пункт 1: Рассмотрим статические методы фабрики вместо конструкторов" в Эффективная Java делает очень убедительные доводы, что статические методы могут быть очень полезны. Он дает яву.утиль.В качестве примера можно привести 32 статических метода фабрики класса Collections.

в одном случае у меня есть иерархия классов POJO, экземпляры которых могут быть автоматически сериализуется в XML и JSON, а затем десериализовать обратно в объекты. У меня есть статические методы, которые используют Java generics для десериализации:fromXML(String xml) и fromJSON(String json). Тип POJO, который они возвращают, не известен априори, но определяется текстом XML или JSON. (Я изначально упаковал эти методы в вспомогательный класс, но было семантически чище переместить эти статические методы в корневой класс POJO.)

еще пара примеров:

  • С помощью класс как пространство имен для группирования связанных методов (например, java.ленг.Математика.)
  • метод действительно является частным вспомогательным методом для конкретного класса без необходимости доступа к переменным экземпляра (случай, приведенный здесь). Просто не крадите this-эквивалент в его список аргументов!

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

нет, использование статики должно быть довольно нишевым.

В этом случае OP, вероятно, "скрывает" состояние в параметрах, переданных в статический метод. Способ постановки вопроса делает это неочевидным (foo() не имеет входов или выходов), но я думаю, что в реальных примерах вещи, которые на самом деле должны быть частью состояния объекта, выпадут довольно быстро.

В конце дня каждый вызов obj.method(param) разрешает method(obj, param), но это продолжается в пути более низкий уровень, чем мы должны проектировать.

Если он когда-либо использовался только методами в A и не имел бы никакого использования вне его, то он должен быть статическим (и, вероятно, быть помещен в вспомогательный класс. Он не имеет никакой пользы за пределами сейчас, но нет никакой гарантии, что он никогда не будет иметь. В противном случае, это не должно.

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

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

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

Если foo является частным, это может быть что угодно, статическое или нет. Но, большую часть времени это будет не статический!--2--> так как это на одно слово меньше для ввода. Затем, если вам нужно использовать состояние, потому что вы изменили свой код, вы можете сделать это сразу.

, когда он защищен и общественным, это зависит от того, что он делает. Эмпирическое правило состоит в том, чтобы сделать это не статический!--6--> когда это не является частью поведения экземпляра, и сделать это статический если это имеет смысл назовите его без каких-либо объектов. Если вы не уверены, спросите себя, имеет ли смысл переопределить метод в подклассе.

Я думаю, что если позволить методам в Java быть статичными, это приведет к довольно хаотичной реализации новичком, который не понимает OO правильно. Мы были там и думали об этом. Если бы методы были статическими по умолчанию, насколько трудно нам понять принцип OO?

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

NB: позвольте мне догадаться, являются вы случайно не читали Код?

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

Если вы, кажется, делаете много вспомогательных методов, то рассмотрите возможность использования методов package-private instance вместо частных. Меньше набирать, менее шаблонный, так как вы можете повторно использовать их в качестве помощника для других классов в том же пакете.

Я думаю, что "private static" (edit: for methods) - это своего рода оксюморон в Java. Основной смысл статических методов, на мой взгляд, заключается в предоставлении доступа к функциям вне контекста экземпляров объектов. Другими словами, они практически всегда полезны, только если они публичны. Если вы вызываете метод только из контекста одного экземпляра объекта, и этот метод является частным, нет смысла делать его статическим. (edit: но это не имеет никакого практического значения).

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

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

  1. вы разбиваете сложный метод на подметоды, или
  2. вы хотите строку (или дату, или...) имел некоторые функциональные возможности, которые он не имеет

первое не плохо само по себе, но это часто признак того, что вам не хватает объектов. Вместо работы с типами по умолчанию, такими как String или List, попробуйте создать свои собственные классы и переместить статические методы в эти классы.

второй разум производит всегда популярные классы StringUtil, DateUtil, FooUtil. Это проблематично, потому что у вас нет способа обнаружить, что они существуют, поэтому программисты часто пишут дубликаты этих методов утилиты. Решение, опять же, состоит в том, чтобы избегать использования строки и даты все время. Начните создавать свои собственные объекты, возможно, обернув исходный объект. Статические методы становятся нестатическими методами нового объекта.

Если foo() не имеет ничего общего с объектом A тогда почему метод там?

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

много интересных ответов.

Если вы отчаянно ищете правило, то используйте это:

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

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

конец истории.