简体   繁体   English

为什么我们在 Java 中使用自动装箱和拆箱?

[英]Why do we use autoboxing and unboxing in Java?

Autoboxing is the automatic conversion that the Java compiler makes between the primitive types and their corresponding object wrapper classes.自动装箱是 Java 编译器在原始类型与其对应的 object 包装类之间进行的自动转换。 For example, converting an int to an Integer, a double to a Double, and so on.例如,将 int 转换为 Integer,将 double 转换为 Double,等等。 If the conversion goes the other way, this is called unboxing.如果转换以另一种方式进行,则称为拆箱。

So why do we need it and why do we use autoboxing and unboxing in Java?那么为什么我们需要它,为什么我们在 Java 中使用自动装箱和拆箱?

Some context is required to fully understand the main reason behind this.需要一些上下文才能完全理解这背后的主要原因。

Primitives versus classes基元与类

Primitive variables in Java contain values (an integer, a double-precision floating point binary number, etc). Java 中的原始变量包含值(整数、双精度浮点二进制数等)。 Because these values may have different lengths , the variables containing them may also have different lengths (consider float versus double ).因为这些值可能有不同的长度,包含它们的变量也可能有不同的长度(考虑floatdouble )。

On the other hand, class variables contain references to instances.另一方面,类变量包含对实例的引用 References are typically implemented as pointers (or something very similar to pointers) in many languages.在许多语言中,引用通常被实现为指针(或与指针非常相似的东西)。 These things typically have the same size, regardless of the sizes of the instances they refer to ( Object , String , Integer , etc).这些东西通常具有相同的大小,而不管它们引用的实例的大小( ObjectStringInteger等)。

This property of class variables makes the references they contain interchangeable (to an extent).类变量的这个属性使得它们包含的引用可以互换(在一定程度上)。 This allows us to do what we call substitution : broadly speaking, to use an instance of a particular type as an instance of another, related type (use a String as an Object , for example).这允许我们做我们所说的替换:广义上讲, 使用特定类型的实例作为另一个相关类型的实例(例如,使用String作为Object )。

Primitive variables aren't interchangeable in the same way, neither with each other, nor with Object .原始变量不能以相同的方式互换,无论是彼此之间还是与Object The most obvious reason for this (but not the only reason) is their size difference.最明显的原因(但不是唯一的原因)是它们的大小差异。 This makes primitive types inconvenient in this respect, but we still need them in the language (for reasons that mainly boil down to performance).这使得原始类型在这方面不方便,但我们在语言中仍然需要它们(主要归结为性能的原因)。

Generics and type erasure泛型和类型擦除

Generic types are types with one or more type parameters (the exact number is called generic arity ).泛型类型是具有一个或多个类型参数类型(确切的数量称为泛型 arity )。 For example, the generic type definition List<T> has a type parameter T , which can be Object (producing a concrete type List<Object> ), String ( List<String> ), Integer ( List<Integer> ) and so on.例如,泛型类型定义List<T>有一个类型参数T ,它可以是Object (产生一个具体类型List<Object> )、 StringList<String> )、 IntegerList<Integer> )等等.

Generic types are a lot more complicated than non-generic ones.泛型类型比非泛型复杂得多。 When they were introduced to Java (after its initial release), in order to avoid making radical changes to the JVM and possibly breaking compatibility with older binaries, the creators of Java decided to implement generic types in the least invasive way: all concrete types of List<T> are, in fact, compiled to (the binary equivalent of) List<Object> (for other types, the bound may be something other than Object , but you get the point).当它们被引入 Java 时(在其初始版本之后),为了避免对 JVM 进行根本性的更改并可能破坏与旧二进制文件的兼容性,Java 的创建者决定以最小侵入性的方式实现泛型:所有具体类型的List<T>实际上被编译为(二进制等价物) List<Object> (对于其他类型,边界可能不是Object ,但你明白了)。 Generic arity and type parameter information are lost in this process , which is why we call it type erasure .泛型和类型参数信息在这个过程中丢失了,这就是我们称之为类型擦除的原因

Putting the two together将两者放在一起

Now the problem is the combination of the above realities: if List<T> becomes List<Object> in all cases, then T must always be a type that can be directly assigned to Object .现在的问题是上述现实的组合:如果List<T>在所有情况下都变成List<Object> ,那么T必须始终是可以直接分配给Object Anything else can't be allowed.不能允许其他任何事情。 Since, as we said before, int , float and double aren't interchangeable with Object , there can't be a List<int> , List<float> or List<double> (unless a significantly more complicated implementation of generics existed in the JVM).正如我们之前所说, intfloatdouble不能与Object互换,因此不能有List<int>List<float>List<double> (除非在JVM)。

But Java offers types like Integer , Float and Double which wrap these primitives in class instances, making them effectively substitutable as Object , thus allowing generic types to indirectly work with the primitives as well (because you can have List<Integer> , List<Float> , List<Double> and so on).但是 Java 提供了诸如IntegerFloatDouble类的类型,它们将这些原语包装在类实例中,使它们可以有效地替代为Object ,从而允许泛型类型也间接地使用这些原语(因为您可以拥有List<Integer> , List<Float>List<Double>等)。

The process of creating an Integer from an int , a Float from a float and so on, is called boxing .int创建Integer ,从float创建Float等等的过程称为装箱 The reverse is called unboxing .反之称为拆箱 Because having to box primitives every time you want to use them as Object is inconvenient, there are cases where the language does this automatically - that's called autoboxing .因为每次想要将原语用作Object必须装箱很不方便,所以在某些情况下语言会自动执行此操作 - 这称为自动装箱

Auto Boxing is used to convert primitive data types to their wrapper class objects.自动装箱用于将原始数据类型转换为其包装类对象。 Wrapper class provide a wide range of function to be performed on the primitive types.包装类提供了对基本类型执行的广泛功能。 The most common example is :最常见的例子是:

int a = 56;
Integer i = a; // Auto Boxing

It is needed because of programmers easy to be able to directly write code and JVM will take care of the Boxing and Unboxing.之所以需要它,是因为程序员很容易能够直接编写代码,并且 JVM 将负责装箱和拆箱。

Auto Boxing also comes in handy when we are working with java.util.Collection types.当我们使用 java.util.Collection 类型时,自动装箱也派上用场。 When we want to create a Collection of primitive types we cannot directly create a Collection of a primitive type , we can create Collection only of Objects.当我们想要创建原始类型的集合时,我们不能直接创建原始类型的集合,我们只能创建对象的集合。 For Example :例如 :

ArrayList<int> al = new ArrayList<int>(); // not supported 

ArrayList<Integer> al = new ArrayList<Integer>(); // supported 
al.add(45); //auto Boxing 

Wrapper Classes包装类

Each of Java's 8 primitive type (byte,short,int,float,char,double,boolean,long) hava a seperate Wrapper class Associated with them. Java 的 8 种原始类型(byte、short、int、float、char、double、boolean、long)中的每一种都有一个单独的 Wrapper 类与它们相关联。 These Wrapper class have predefined methods for preforming useful operations on primitive data types.这些 Wrapper 类具有预定义的方法,用于对原始数据类型执行有用的操作。

Use of Wrapper Classes包装类的使用

String s = "45";
int a = Integer.parseInt(s); // sets the value of a to 45.

There are many useful functions that Wrapper classes provide. Wrapper 类提供了许多有用的功能。 Check out the java docs here 在此处查看 Java 文档

Unboxing is opposite of Auto Boxing where we convert the wrapper class object back to its primitive type.拆箱与自动装箱相反,我们将包装类对象转换回其原始类型。 This is done automatically by JVM so that we can use a the wrapper classes for certain operation and then convert them back to primitive types as primitives result int faster processing.这是由 JVM 自动完成的,因此我们可以使用包装类进行某些操作,然后将它们转换回原始类型,因为原始类型会导致更快的处理。 For Example :例如 :

Integer s = 45;
int a = s; auto UnBoxing;

In case of Collections which work with objects only auto unboxing is used.对于与对象一起使用的集合,仅使用自动拆箱。 Here's how :就是这样 :

ArrayList<Integer> al = new ArrayList<Integer>();
al.add(45);

int a = al.get(0); // returns the object of Integer . Automatically Unboxed . 

The primitive (non-object) types have there justification in efficiency.原始(非对象)类型在效率上是有道理的。

The primitive types int, boolean, double are immediate data, whereas Object s are references.原始类型int, boolean, double是直接数据,而Object是引用。 Hence fields (or variables)因此字段(或变量)

int i;
double x;
Object s;

would need local memory 4+8+8?需要4+8+8的本地内存吗? where for the object only the reference (address) to memory is stored.对于对象,仅存储对内存的引用(地址)。

Using the Object wrappers Integer, Double and others, one would introduce an indirection, reference to some Integer/Double instance in the heap memory.使用对象包装器Integer, Double和其他包装器Integer, Double可以引入一种间接引用,即对堆内存中某个 Integer/Double 实例的引用。

Why boxing is needed?为什么需要拳击?

That is a question of relative scope.这是一个相对范围的问题。 In a future java it is planned to be able to have an ArrayList<int> , lifting primitive types.在未来的 java 中,计划能够有一个ArrayList<int> ,提升原始类型。

Answer: For now an ArrayList only works for Object, reserving room for an object reference, and managing garbage collection likewise.答:目前 ArrayList 仅适用于 Object,为对象引用保留空间,并同样管理垃圾收集。 Hence generic types are Object children.因此泛型类型是 Object 的孩子。 So if one wanted an ArrayList of floating point values, one needed to wrap a double in a Double object.因此,如果想要一个浮点值的 ArrayList,则需要将一个 double 包装在一个 Double 对象中。

Here Java differs from the traditional C++ with its templates: C++ classes vector<string>, vector<int> would create two compilation products.这里 Java 与传统 C++ 的不同之处在于其模板:C++ 类vector<string>, vector<int>将创建两个编译产品。 Java design went for having one ArrayList.class, not needing for every parameter type a new compiled product. Java 设计倾向于拥有一个 ArrayList.class,而不需要为每个参数类型都创建一个新的编译产品。

So without boxing to Object one would need to compile classes for every occurrence of a parameter type.因此,如果不对 Object 进行装箱,则需要为每次出现的参数类型编译类。 In concreto: every collection or container class would need a version for Object, int, double, boolean.具体来说:每个集合或容器类都需要一个 Object、int、double、boolean 版本。 The version for Object would handle all child classes. Object 的版本将处理所有子类。

In fact, the need for such diversification already existed in Java SE for IntBuffer, CharBuffer, DoubleBuffer, ... which operate on int, char, double.事实上,Java SE 中已经存在对 IntBuffer、CharBuffer、DoubleBuffer 等多样化的需求,它们对 int、char、double 进行操作。 It was solved in a hacky way by generating these sources from a common one.通过从一个共同的来源生成这些来源,它以一种hacky的方式解决了。

Starting with JDK 5, java has added two important functions: autoboxing and autounboxing.从JDK 5开始,java增加了两个重要的功能:自动装箱和自动拆箱。 AutoBoxing is the process for which a primitive type is automatically encapsulated in the equivalent wrapper whenever such an object is needed. AutoBoxing是在需要此类对象时将基本类型自动封装在等效包装器中的过程。 You do not have to explicitly construct an object.您不必显式构造对象。 Auto-unboxing is the process whereby the value of an encapsulated object is automatically extracted from a type wrapper when its value is required.自动拆箱是当需要封装对象的值时,自动从类型包装器中提取其值的过程。 You do not need to call a method such as intValue() or doubleValue() .您不需要调用诸如intValue()doubleValue() 之类的方法

The addition of autoboxing and auto-unboxing greatly simplifies writing algorithms , eliminating the bait manually boxing and unboxing of values.自动装箱和自动拆箱的加入大大简化了算法的编写,消除了手动装箱和拆箱值的诱饵。 It is also helpful to avoid mistakes .避免错误也很有帮助。 It is also very important for generics , who only operate on objects.对于只对对象进行操作的泛型也非常重要。 Lastly, autoboxing facilitates work with the Collections Framework .最后,自动装箱有助于使用集合框架

why do we have (un)boxing?为什么我们有(取消)拳击?

to make writing code where we mix primitives and their Object Oriented (OO) alternatives more comfortable/less verbose.使我们在混合原语及其面向对象 (OO) 替代方案的情况下编写代码更舒适/更简洁。

why do we have primitives and their OO alternatives?为什么我们有原语及其面向对象的替代品?

primitive types are not classes (unlike in C#), thus they are not subclasses of Object and can not be overridden.原始类型不是类(与 C# 不同),因此它们不是Object子类,不能被覆盖。

we have primitives like int for performance reasons, and the Object alternatives like Integer for the benefits of OO programming, and as a minor point, to have a good location for utility constants and methods (Integer.MAX_VALUE and Integer.toString(int) ).出于性能原因,我们有像int这样的原语,还有像Integer这样的Object替代方案,是为了 OO 编程的好处,并且作为一个小点,为实用程序常量和方法(Integer.MAX_VALUE 和Integer.toString(int) )提供了一个好的位置.

The OO benefits are visible most easily with Generics ( List<Integer> ), but are not limited to that, for example:使用泛型( List<Integer> )最容易看到面向对象的好处,但不仅限于此,例如:

Number getMeSome(boolean wantInt) {

    if (wantInt) {
        return Integer.MAX_VALUE;
    } else {
        return Long.MAX_VALUE;
    }
}

Some data structures can accept only objects, no primitive types.一些数据结构只能接受对象,不能接受原始类型。

Example: the key in a HashMap.示例:HashMap 中的键。

See this question for more: HashMap and int as key有关更多信息,请参阅此问题: HashMap and int as key

There are other good reasons, such as a "int" field in a database, which could be NULL as well.还有其他很好的理由,例如数据库中的“int”字段也可能为 NULL。 An int in Java cannot be null ; Java 中的 int 不能为 null ; an Integer reference can.整数引用可以。 Autoboxing and unboxing provide with a facility to avoid writing extraneous code in the conversions back and forth.自动装箱和拆箱提供了一种便利,可以避免在来回转换中编写无关代码。

ArrayList does not support primitive types only support class. ArrayList 不支持原始类型,只支持类。 but we need to use primitive types eg int, double etc.但是我们需要使用原始类型,例如 int、double 等。

ArrayList<String> strArrayList = new ArrayList<String>(); // is accepted.

ArrayList<int> intArrayList = new ArrayList<int>(); // not accepted.

The Integer class wraps a value of the primitive type int in an object.so bellow code is accepted. Integer 类将原始类型 int 的值包装在一个对象中。因此可以接受以下代码。

ArrayList<Integer> intArrayList = new ArrayList<Integer>(); // is accepted.

we can add a value with add(value) method.我们可以使用 add(value) 方法添加一个值。 To add a String value say "Hello" in strArrayList code is just要添加字符串值,在 strArrayList 代码中说“Hello”只是

strArrayList.add("Hello");  

and add a int value say 54 we can write并添加一个 int 值说 54 我们可以写

intArrayList.add(54);

but when we write intArrayList.add(54);但是当我们写 intArrayList.add(54); compiler convert to the following line编译器转换为以下行

intArrayList.add(Integer.valueOf(54)); 

As intArrayList.add(54) is easy and more acceptable from user side so compiler does the hard job which is ` intArrayList.add(Integer.valueOf(54));由于 intArrayList.add(54) 很容易并且更容易被用户接受,所以编译器完成了艰苦的工作,即intArrayList.add(Integer.valueOf(54)); it is autoBoxing.它是自动装箱。

Similarly to retrieve value we just type intArrayList.get(0) and compiler convert to <code>intArrayList.get(0).intValue();与检索值类似,我们只需键入 intArrayList.get(0) 并编译器转换为<code>intArrayList.get(0).intValue(); which is autoUnboxing.这是自动拆箱。

Because they are different types, and as a convenience.因为它们是不同的类型,并且为了方便。 Performance is likely the reason for having primitive types.性能可能是使用原始类型的原因。

Autoboxing: Converting a primitive value into an object of the corresponding wrapper class.自动装箱:将原始值转换为相应包装器类的对象。

Unboxing: Converting an object of a wrapper type to its corresponding primitive value拆箱:将包装类型的对象转换为其相应的原始值

// Java program to illustrate the concept 
// of Autoboxing and Unboxing 
import java.io.*; 

class GFG 
{ 
    public static void main (String[] args) 
    { 
        // creating an Integer Object 
        // with value 10. 
        Integer i = new Integer(10); 

        // unboxing the Object 
        int i1 = i; 

        System.out.println("Value of i: " + i); 
        System.out.println("Value of i1: " + i1); 

        //Autoboxing of char 
        Character gfg = 'a'; 

        // Auto-unboxing of Character 
        char ch = gfg; 
        System.out.println("Value of ch: " + ch); 
        System.out.println("Value of gfg: " + gfg); 

    } 
} 

Another Special Case is,另一个特例是,

Integer intval = null;
int toPrimitive = intval;
System.out.println(toPrimitive);

We are getting NullPointerException for above scenario.对于上述情况,我们得到了NullPointerException that means we can catch NPE这意味着我们可以捕获 NPE

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM