【问题标题】:Can I pass a primitive type by reference in Java?我可以在 Java 中通过引用传递原始类型吗?
【发布时间】:2011-04-09 09:41:17
【问题描述】:

我想调用一个可能采用不同版本的方法,即输入参数类型相同的方法:

  • 布尔值
  • 字节
  • int

我想这样做的方法是“重载”方法(我认为这是正确的术语?):

public void getValue(byte theByte) {...}
public void getValue(short theShort) {...}
... etc ...

...但这意味着我必须通过引用传递原始类型...类似于 C++,其中该方法具有外部效果,它可以在其范围之外修改变量。

有没有办法在不创建新类或使用原始类型的 Object 版本的情况下做到这一点?如果没有,有什么替代策略的建议吗?

如果我应该进一步解释以消除任何混淆,请告诉我。


更新

我实际上想要做的是从一组位构造原始类型。因此,如果我正在处理该方法的字节版本,我几乎想要完成我的工作以获取 8 位并返回字节(因为我无法通过引用传递)。

我问这个问题的原因是因为我对位所做的工作非常重复,我不想在不同的方法中使用相同的代码。所以我想找到一种方法让我的 ONE 方法知道我在谈论多少位......如果我正在使用一个字节,那么 8 位,如果我正在使用一个短的 16 位等...

【问题讨论】:

    标签: java pass-by-reference overloading primitive-types


    【解决方案1】:

    Java is always pass-by-value。 Java 中没有传递引用。 It's written in the specs!

    【讨论】:

    • 学究气,是传值。您获得了相同的对象(对于非原始对象),但您无法在自己的范围之外更改实际引用。
    • 哪个程序员会阅读规范而不是去 SO?去 SO 回答这样的问题比通过规范要快得多。如果程序员宁愿通过规范来解决这样的问题,那程序员的效率就很低了。
    【解决方案2】:

    虽然 Java 支持重载,但所有参数都是按值传递的,即分配方法参数对调用者不可见。

    从您的代码 sn-p 中,您尝试返回不同类型的值。由于返回类型不是方法签名的一部分,因此您不能使用不同的返回类型进行重载。因此,通常的做法是:

    int getIntValue() { ... }
    byte getByteValue() { ... }
    

    如果这实际上是一个转换,标准命名是

    int toInt() { ...}
    byte toByte() { ... }
    

    【讨论】:

    • +1 是的,我打败了它。看起来 OP 的目标是避免这种情况,因为它是一种完全常见且合法的模式。
    • 请参阅我的更新,了解我为什么要避免这种情况。现在在我看来,我最终会这样做,但值得一问。
    • 是的,即使阅读您的更新,我也会推荐我的方法。调用者必须以某种方式指定他们想要的位数,并且在方法名称中拼写出来非常清楚。
    • 很公平。感谢您的建议!
    【解决方案3】:

    你不能。在 Java 中,参数总是按值传递。如果参数是引用类型,则引用是按值传递的,您可以在方法内部对其进行修改,而对于原始类型,这是不可能的。

    您需要创建一个包装器类型。

    【讨论】:

    • 好的 :( 我将如何创建这个“包装器类型”?你能给我看一些代码吗?
    • @Hristo,你可以创建一个类来保存原始类型并将这个类传递给方法。
    • 嗯...好的。我会看看这个。谢谢
    • 使用这种包装器时间感觉就像使用AtomicBooleanAtomicInteger 等。当然没有并发/阻塞开销。
    • @whiskeysierra 所以盒装课程?像整数。
    【解决方案4】:

    原语不是通过引用(或相关的对象)传递的,所以你不能。

    int i = 1;
    moo(i);
    public void moo(int bah)
    {
       bah = 3;
    }
    System.out.println(i);
    

    打印出 1

    【讨论】:

    • 是的,我明白这一点。有其他选择吗?
    【解决方案5】:

    如果我没记错的话,Java 中原始类型的对象类型(Double、Integer、Boolean 等)是不可变的。这意味着您无法更改传入方法中的原始值。

    对此有两种解决方案。一种是制作一个保存该值的包装器类型。如果您尝试做的只是更改该值或从该值获取计算,您可以让该方法为您返回结果。举个例子:

    public byte getValue(byte theByte) {...}
    public short getValue(short theShort) {...}
    

    您可以通过以下方式调用它们:

    Short s = 0;
    s = foo.getValue(s);
    

    或类似的东西。这允许你改变或改变值,并返回改变后的值,这将允许如下内容:

    Short s = foo.getValue(10);
    

    希望对您有所帮助。

    【讨论】:

      【解决方案6】:

      如果您想使用原语,我想说的替代策略是执行 Java 库所做的事情。只是把它吸起来,有多种方法。

      例如ObjectInputStream有readDouble()readByte()

      通过共享函数的实现,您不会获得任何收益,而您的函数的客户端也不会通过具有相同名称的函数变体获得任何收益。

      更新

      考虑到您的更新,我认为没有必要复制太多代码。这取决于您的编码策略,但我想您可以这样做:

      private byte get8Bits();
      public byte getByte() {
          return get8Bits();
      }
      public int getInt() {
          return (get8Bits() << 24) | (get8Bits() << 16) | (get8Bits() << 8) | get8Bits();
      }
      

      任何共享代码超过此值的东西都可能是过度设计。

      另一种可能是

      private long getBits(int numBits);
      
      public byte getByte() {
          return (byte)getBits(8);
      }
      
      public int getInt() {
          return (int)getBits(32);
      }
      

      即我认为将库的用户暴露给原始类型本身以外的任何东西都是没有意义的。

      如果您真的非常想这样做,那么您可以创建一个这样的访问方法:

      @SuppressWarnings("unchecked")
      public static <T> T getValue(Class<T> clazz) {
          if ( clazz == byte.class ) {
              return (T)Byte.valueOf((byte)getBits(8));
          } else if ( clazz == int.class ) {
              return (T)Integer.valueOf((int)getBits(32));
          }
          throw new UnsupportedOperationException(clazz.toString());
      }
      
      //...
      byte b = getValue(byte.class);
      int i = getValue(int.class);
      

      但是我看不出它对你图书馆的客户来说是多么的不那么麻烦。

      【讨论】:

      • 哈哈哈......够公平的。如果我没有找到让我满意的解决方案,我会采用多种方法。
      【解决方案7】:

      听起来您有一组要解析的位。您应该将它包装在一个对象中,让我们将该对象称为 BitSet。您正在遍历这些位,因此您将拥有类似 Iterator 的东西,并且您想解析出字节、整数、长整数等...对吗?

      然后你将拥有你的 Parser 类,它上面有如下方法:

      public byte readByte(Iterator<Bit> bitit) {
        //reads 8 bits, which moves the iterator forward 8 places, creates the byte, and returns it
      }
      public int readInt(Iterator<Bit> bitit) {
        //reads 32 bits, which moves the iterator forward 32 places, creates the int, and returns it
      }
      

      等等……

      因此,在调用所需的任何方法后,您已经以类型安全的方式提取了所需的值(不同方法的返回类型不同),并且迭代器已根据类型向前移动了正确的位置数.

      这就是你要找的吗?

      【讨论】:

      • 实际上...这不是一个坏主意...但我将使用 InputStream。然而,这是一个有趣的建议。谢谢。
      • 好吧,如果您使用的是 InputStream,那就更简单了!您只需将它传递给您的各种函数并根据需要读取尽可能多的字节。每次调用函数时,您在流中的位置都会向前移动,该字节数与该函数所需的字节数相同。像馅饼一样容易!
      【解决方案8】:

      只有通过创建自己的价值持有类型。

      【讨论】:

      • 包装器类型是包含您要更改的值的对象。它们通常用于将相关值汇集到一个可以更轻松地传递的对象中。
      【解决方案9】:

      是的,请更具体地说明您想要实现的目标。 根据您的描述,我建议您查看 Java 泛型,您可以在其中编写如下内容:

      class SomeClass <GenericType> {
        GenericType val;  
      
        void setValue(GenericType val) {
           this.val = val;
        }
      
        GenericType getValue() {
           return val;
        }
      
        public static void main(String[] args) {
          SomeClass<Integer> myObj = new SomeClass<Integer>();
          myObj.setValue(5);
          System.out.println(myObj.getValue());
      
          SomeClass<String> myObj2 = new SomeClass<String>();
          myObj2.setValue("hello?!");
          System.out.println(myObj2.getValue());
      
        }
      
      }
      

      【讨论】:

      • 我在帖子中添加了更新。请参阅它以获取更多信息。
      猜你喜欢
      • 1970-01-01
      • 2014-08-22
      • 2020-09-08
      • 2023-03-22
      • 2015-02-22
      • 2011-05-18
      • 2015-09-15
      • 1970-01-01
      相关资源
      最近更新 更多