2017-01-28 23 views
-1

Предположим, у вас есть класс объекта с тремя целыми полями , которые вы хотите изменить, одинаково одним способом.int vs Integer vs пользовательский класс для достижения эффективного прохождения по эталонному поведению в Java

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

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

Это невозможно достичь в Java с помощью примитивного типа «ИНТ» для тех, поля.
Я знаю о том, что Java «всегда» передается по значению, а не проходит по ссылке - и - я слышал, как в сети существует одна из причин, по которой существует класс Integer, а также другой объект «wrapper» "классы для обычных примитивных типов, таких как int и double.

Отправка объекта в качестве аргумента в метод должна теоретически предоставлять способ [эффективно, если не технически] передавать по ссылке, поскольку переданное значение, предположительно, является значением ссылки на объект , Очень сложно. НО - и это то, куда приходит мое раздражение. Я попытался достичь этой очень простой задачи, передав аргумент Integer вместо int, и желаемое поведение все еще не было выполнено. 1 не добавлено в соответствующее поле.

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

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

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

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

Таким образом, я предполагаю, что конечный вопрос: Почему Integer не функционирует разумным образом? Какой смысл обертывать примитив в объекте вообще, если он даже не помогает выполнить что-то такое простое? Я пропустил что-то простое о том, как заставить Integer функционировать желаемым образом? (Надеюсь, что так). Ответ кажется закрытым, но в ярости недоступным, поскольку «RInteger» создает желаемое поведение, но «Integer» этого не делает.

Весь исходный код, который я использовал при попытке выяснить, как построить этот кропотливый вопрос, приведен ниже.

package r9mp; 

import javax.swing.SwingUtilities; 

public class RefTest2 { 
//[main m] 
public static void main(String[] args){ 
    SwingUtilities.invokeLater(new Runnable(){ 
     public void run(){ 
      new RefTest2(); 
     } 
    }); 
} 
//[fields] 
int i; 
Integer I; 
RInteger RI; 

//[constr] 
public RefTest2(){ 
    intTest(); 
    IntegerTest(); 
    RIntegerTest(); 

    display(); 
} 

//[methods] 
private void intTest(){ 
    i = 100; 
    intMethod(i); 
} 
private void IntegerTest(){ 
    I = 100; //boxing? auto? 
    IntegerMethod(I); 
    I = 100; //just in case. 
    IntegerMethod2(I); 
} 
private void RIntegerTest(){ 
    RI = new RInteger(100); 
    RIntegerMethod(RI); 
} 

private void intMethod(int ipar){ 
    ipar = ipar + 1;//no change. expected. 
} 
private void IntegerMethod(Integer IPar){ 
    IPar = IPar + 1;//no change. frustrating. 
    pln("From inside IntegerMethod: IPar = " + IPar); 
    pln("From inside IntegerMethod: I = " + I); 
} 
private void IntegerMethod2(Integer IPar){ 
    IPar = new Integer(IPar+1);//still no change. there are no set methods for Integer, or I'd try them. 
} 
private void RIntegerMethod(RInteger riPar){ 
    riPar.value = riPar.value + 1; 
} 

private void display(){ 
    pln(
      "Display... \n" + 
      "i: " + i + "\n" + 
      "I: " + I + "\n" + 
      "RI: " + RI + "\n" + 
      "--------" 
      ); 
} 

private void pln(){ 
    pln(""); 
} 
private void pln(String s){ 
    System.out.println(s); 
} 

//[internal class] 
private class RInteger{ 
    int value; 
    public RInteger(int v){ 
     value = v; 
    } 
    public String toString(){ 
     return ""+value; 

    } 
} 

} 

И вот выход ... RefTest2 output

+0

Привет Райан, вы, кажется, использует свой класс Java 'RefTest2' странным образом. Как правило, класс будет иметь поля, которые обновляются. Поэтому не имеет значения, передаются ли переменные метода setter по ссылке или передаются по значению, потому что мы используем их только как поля для чтения для передачи информации. –

+1

Если вы хотите написать метод с побочными эффектами (что уже довольно плохо), используйте массив. Довольно очевидное обходное решение. – Tom

+1

Поскольку 'Integer' является неизменным, он в основном ведет себя так же, как' int' делает с точки зрения передачи его методам и его изменения. Вы не можете изменить значение внутри 'Integer', вы можете создавать только новый экземпляр, который затем переопределяет переданное значение и оставляет прежнюю ссылку в такте. – luk2302

ответ

1

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

После того, как вы прочитали об этом, прочитайте статьи Эрика Липперта о неизменности. Начать здесь: https://blogs.msdn.microsoft.com/ericlippert/2007/11/13/immutability-in-c-part-one-kinds-of-immutability/ Mind = blown.

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

Вы можете достичь того, чего хотите, с классом своего собственного устройства, которое играет роль ссылки или просто передаёт массив и модифицирует элемент по адресу array[0].

Мои личные предпочтения следующим образом:

  • Я хотел бы попробовать сделать как можно больше с возвращаемых значений.
  • Если возвращаемые значения неприменимы (в данном случае с invokeLater,), то мои следующие предпочтения будут иметь внутренние/вложенные/анонимные классы, имеющие доступ к полям охватывающего класса.
  • Если это не вариант, то специальными классами, созданными именно для приложения под рукой, являются мой следующий вариант. (MyMutableNumberWrapper.)
  • И когда я просто хочу что-то быстрое и грязное, тогда моими основными параметрами станут классы общего назначения, такие как Ref<T> (или даже одноэлементные массивы).
+0

Я знал, что массив также достигнет желаемого поведения ... на самом деле это было частью того, что я тестировал в RefTest, но я не включил его в RefTest2. Я несколько неохотно использую массивы, просто потому, что это немного более запутанная путаница, на которую можно смотреть.Если пользовательские объекты - это то, что действительно нужно, для достижения такого рода вещей, я полагаю, это то, что я в конечном итоге сделаю. Что касается хэш-карт или карт в Java вообще, я знаю их, но я уже почти не экспериментировал с ними. –

+0

Я согласен, массивы - это неуклюжее и хакерское решение проблемы. Я немного изменил свой ответ. –

+1

Я уверен, что во фразе «Java всегда передает аргументы по значению». Это правильно для примитивов, но для объектов Java всегда передает ссылку на объект в качестве аргумента. Объектом этого не является. Чтобы сделать его совместимым с примитивами, их обертки неизменны и позволяют иметь код «Integer a = 100;» вместо «Integer a = new Integer (100);' – Vadim

1

Как насчет одного метода для примитивов и их оберток?

private int incInteger(int value) 
{ 
    return value + 1;  
} 

и призывают к ней:

int intVal = 100; 
intVal = incInteger(intVal); 

Integer integerVal = 200; 
integerVal = incInteger(integerVal); 
+0

Возможно, это лучший результат ... по крайней мере, если бы я был уверен, что это лучший способ сделать что-то во всех или в большинстве случаев. Вместо того, чтобы выполнять всю вещь отдельно в методе, ваше решение ставит фактическую часть, определяющую значение, вне ее, с помощью «intVal =» и «integerVal =». Если такая штука окажется лучшим/единственным решением, тогда мне придется приложить усилия, чтобы забить ее в мозг, чтобы закодировать этот путь, чтобы не давать мне немного другого выбора, кроме как писать тонны почти одинаковых методов ... –

+1

Это хорошо для примитивов. Для других классов плохой идеей является замена объекта новым экземпляром внутри метода. Если это необходимо, это должно быть сделано в коде вызывающего абонента. Конечно, внутри свойства метода передаваемого объекта можно изменить. Это обычная практика. Кроме того, во всем мире существует множество таких вещей, как «Держатель» с значением объекта в нем, когда оно требуется. Ваш класс RInteger является «Держателем». Но это не нужно для примитивов. – Vadim