Почему этот метод должен быть статическим (Java)?

В качестве некоторой предыстории, я сейчас нахожусь в главе 8 моей книги, мы закончили говорить об массивах, массивах, операторах if, циклах и т. Д. Теперь в этой части книги говорится о вызове по ссылке, значении и некоторых других довольно интересных вещах, которые кажутся Сначала это было странно. Я прочитал Какую ситуацию использовать static и некоторые другие вопросы SO, а также узнали немало.

Рассмотрим следующий пример, приведенный в моей книге (среди множества примеров)

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

public class Geometry
{
  public static double area(Rectangle rect)
  {
    return rect.getWidth() * rect.getHeight();
  }
// More geometry methods can be added here.
}

Теперь мы можем сказать вам, почему основной метод статический. При запуске программы никаких объектов нет. Следовательно, первый метод в программе должен быть статическим.

Хорошо, это довольно круто, до сих пор я просто слепо ставил public перед всеми своими методами, так что это здорово знать. Но мое внимание привлекла небольшая проблема с обзором на следующей странице.

Следующий метод вычисляет среднее значение массива чисел:

public static double average(ArrayList<Double> values)

Почему это должен быть статический метод?

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

import java.util.ArrayList;
class ArrList
{
    private double sum;

    public ArrList()
    {
        sum = 0;
    }

    public double average(ArrayList <Double> values)
    {
        for(int i = 0; i < values.size() ; i++)
        {
            sum+=values.get(i);
        }

        return sum / values.size();

    }
}

public class Average
{
    public static void main(String [] args)
    {
        ArrList arrListObj = new ArrList();
        ArrayList<Double> testArrList = new ArrayList<Double>();
        testArrList.add(10.0);
        testArrList.add(50.0);
        testArrList.add(20.0);
        testArrList.add(20.0);
        System.out.println(arrListObj.average(testArrList));

    }
}

TL; DR

Почему в моей книге сказано, что public static double average(ArrayList<Double> values) должен быть статичным?

ПОПЫТКА ПРИ ИСПОЛЬЗОВАНИИ СТАТИЧЕСКИХ ДАННЫХ

public class Average
{
    public static void main(String [] args)
    {
        ArrayList<Double> testArrList = new ArrayList<Double>();
        ArrayList<Double> testArrListTwo = new ArrayList<Double>();
        testArrList.add(10.0);
        testArrList.add(50.0);
        testArrList.add(20.0);
        testArrList.add(20.0);
        testArrListTwo.add(20.0);
        testArrListTwo.add(20.0);
        testArrListTwo.add(20.0);
        System.out.println(ArrList.average(testArrList));
        System.out.println(ArrList.average(testArrListTwo)); // we don't get 20, we get 53.3333!


    }
}

person Muntasir Alam    schedule 11.08.2016    source источник
comment
Вам никогда не понадобится static. Но нет реальной причины не использовать его, поскольку он позволяет избежать создания экземпляра ArrList. Кроме того, ваша реализация выйдет из строя, если вы попытаетесь использовать один и тот же экземпляр ArrList для вычисления нескольких средних значений.   -  person resueman    schedule 11.08.2016
comment
«Потребность» - сильное слово, но я бы сказал, что это должно быть   -  person Kevin L    schedule 11.08.2016
comment
ваш пример ArrList не имеет смысла. у него есть переменная экземпляра, которая инициализируется только в конструкторе, несколько вызовов вашего метода в одном экземпляре приведут к мусору.   -  person Nathan Hughes    schedule 11.08.2016
comment
@NathanHughes, не могли бы вы расшириться? Не совсем уверен, что ты имеешь в виду, извини   -  person Muntasir Alam    schedule 11.08.2016
comment
@cresjoy: используйте свой метод average дважды, с двумя разными ArrayList, и посмотрите, что произойдет.   -  person David    schedule 11.08.2016
comment
@cressjoy Если вы добавите список (скажем, [10,15,20]) к своему объекту и вычислите среднее значение, он правильно выдаст 15. с использованием того же объекта, если вы добавите еще один список (скажем, [1,2,3]), arrListObject.average([1,2,3]) не выдаст 2, как ожидалось, из-за предыдущего состояния - вместо этого вы получите 17.   -  person Kevin L    schedule 11.08.2016
comment
Итак, как статика решает эту проблему, @Kevin, я понимаю, о чем вы?   -  person Muntasir Alam    schedule 11.08.2016
comment
@cresjoy: static не сам по себе решает эту проблему. Устранение зависимости состояния, внешнего по отношению к методу (переменной экземпляра), решает проблему. Если нет состояния, внешнего по отношению к методу, это потенциальный кандидат на роль static метода, если семантически имеет смысл сделать его таковым.   -  person David    schedule 11.08.2016
comment
Я понимаю. Я изменил все на статическое в моем новом редактировании, но мое новое среднее значение не то, что должно быть, это та же проблема, которую предлагал @Kevin?   -  person Muntasir Alam    schedule 11.08.2016
comment
вы все еще храните сумму как статическую переменную? проблема с вашей старой реализацией в том, что sum никогда не сбрасывался каждый раз   -  person Kevin L    schedule 11.08.2016
comment
@cresjoy: Похоже, вы добавили ключевое слово static и надеялись, что произойдет волшебство, но на самом деле не устранили проблему сохранения состояния вне метода.   -  person David    schedule 11.08.2016
comment
Да, я забыл обнулить сумму. Но если я сброшу сумму до нуля и уберу статичность, новое среднее значение, как вы сказали, все равно будет испорчено !! Вау, не ожидал этого, теперь я наконец понял, что ты имеешь в виду   -  person Muntasir Alam    schedule 11.08.2016
comment
Можно ли с уверенностью сказать, что в этом случае статика является более или менее вопросом техники и хорошей практики кодирования, а не фактической функцией статического кода, а не самой собой?   -  person Muntasir Alam    schedule 11.08.2016


Ответы (3)


Это не так.

Единственный метод, который должен быть static, - это начальный main() метод. Все остальное зависит от вас как от программиста, чтобы решить, что имеет смысл в вашем дизайне.

static не имеет ничего общего с public аксессорами (как вы намекаете) и не имеет ничего общего с выполняемой технической операцией. Это все связано с семантикой операции и классом, который ее держит.

Экземплярный (не static) метод существует в конкретном экземпляре класса. Семантически он должен выполнять операции, связанные с этим конкретным экземпляром. Метод static существует для класса в целом и является более концептуальным. Он ничего не делает с конкретным экземпляром (если, конечно, он не предоставил экземпляр чего-либо в качестве аргумента метода).

Так что вам просто нужно спросить себя о семантике операции. Нужно ли вам new экземпляр объекта для выполнения операции? Или операция должна быть доступна без инстанса? Это зависит от операции, от того, что представляют собой объекты и т. Д.

person David    schedule 11.08.2016
comment
Полагаю, в моем учебнике говорилось, почему, судя по ответам, настоятельно рекомендуется, чтобы этот метод был статическим. Я не совсем понимаю, в первом примере мы сделали его статическим, потому что в библиотеке Rectangle нет функции области. Здесь та же идея? Мы делаем его статичным, так как нет встроенного метода усреднения? - person Muntasir Alam; 11.08.2016
comment
@cresjoy: Да, похоже, это та же концептуальная причина. Есть много способов организовать эти операции, не только static по сравнению с экземпляром. - person David; 11.08.2016
comment
Хм, я чувствую, что все упражнения, которые я выполняю сейчас, включают выполнение некоторых операций с массивами (например, получение суммы, затем выполнение чего-то и т.д.), означает ли это, что я должен был сделать все свои методы статическими, как раньше? - person Muntasir Alam; 11.08.2016
comment
@cresjoy: Может, не знаю? Лучший ответ на это: если это имеет смысл, то да. Нет универсального правила, когда что-то должно быть static. Семантика того, что вы создаете, может быть очень субъективной. Возможно имеет смысл иметь несколько static методов. Может иметь смысл иметь какой-либо объект, который несет состояние уровня экземпляра и выполняет операции. И Т. Д. - person David; 11.08.2016
comment
В порядке. Конечно, этот материал кажется довольно многословным и сложным: / Спасибо - person Muntasir Alam; 11.08.2016

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

Из другого класса:

Average.average(new ArrayList<Double>()); // legal only if static
new Average().average(new ArrayList<Double>()); // necessary if not static
// and only makes sense if Average can be instantiated in the first place
  1. законно делать это переменной экземпляра (т. Е. Не статической), но этот метод на самом деле сложнее понять. Если это static, то всякий, кто читает код, знает, что он не использует никаких переменных-членов класса.

    // In the class body
    int x = 0; // member variable
    public static double average() {
        x = x + 1; // illegal
    }
    

Чем меньше что-то может сделать, тем легче понять, что это делает.

person djechlin    schedule 11.08.2016
comment
Похоже, что в прошлом я делал все публично, так как я действительно не знал, что делают статические. Похоже, что все лучше статично. Должен ли я тогда просто поставить статику на большинство методов? Кажется проще - person Muntasir Alam; 12.08.2016

Статические методы, такие как area, average, обычно являются служебными функциями. Вам не нужен какой-либо объект для использования служебной функции. Например, рассмотрим Math. pow вам не нужно создавать экземпляры какого-либо объекта для использования функции мощности, просто используйте Math.pow(10.0, 2.0), чтобы получить (10.0)^2

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

person SomeDude    schedule 11.08.2016