Java的有损转换

1,762 阅读5分钟

1. 概述

在这个快速教程中,我们将讨论Java中有损转换的概念及其背后的原因。

同时,我们将探索一些方便的转换技术来避免这个错误。

2. 有损转换

有损转换就是处理数据时信息的丢失。

在Java中,它对应于在将一种类型转换为另一种类型时丢失变量值或精度的可能性。

当我们尝试将一个高级类型的变量转换成一个低级类型时,Java将在编译代码时生成一个错误的、不兼容的类型:可能的有损转换。

例如,让我们尝试将一个long值赋给一个int值:

long longNum = 10;
int intNum = longNum;

在编译此代码时,Java会产生一个错误:

incompatible types: possible lossy conversion from long to int

在这里,Java将发现long和int不兼容,并导致有损转换错误。因为在int范围-2,147,483,648到2,147,483,647之外可以有long值。

类似地,我们试着给一个 long 赋给一个 float :

float floatNum = 10.12f;
long longNum = floatNum;
incompatible types: possible lossy conversion from float to long

因为float可以表示一些没有相应long类型的小数值。因此,我们将收到相同的错误。

同样,将一个 double 转换成 int 也会导致相同的错误:

double doubleNum = 1.2;
int intNum = doubleNum;
incompatible types: possible lossy conversion from double to int

double值对于int值来说可能太大或太小,小数值将在转换中丢失。因此,这是一个潜在的有损转换。

此外,在执行简单计算时,我们可能会遇到此错误:

int fahrenheit = 100;
int celcius = (fahrenheit - 32) * 5.0 / 9.0;

当一个double值乘与一个int值,我们得到的结果是一个double值。因此,它也是一个潜在的有损转换。

因此,有损转换中不兼容的类型可以有不同的大小或类型(整数或小数)。

3. 基本数据类型

在Java中,有许多基本数据类型和它们对应的包装类。

接下来,让我们列出一个Java中所有可能的有损转换的列表:

  • short to byte or char
  • char to byte or short
  • int to byte, short or char
  • long to byte, short, char or int
  • float to byte, short, char, int or long
  • double to byte, short, char, int, long or float

请注意,虽然short和char的范围相同。不过,从short到char的转换是有损的,因为char是无符号数据类型。

4. 转换技术

4.1. 两个基本数据类型之间的转换

基本类型避免有损转换的简单方法是通过向下强制转换;换句话说,将高级类型强制转换为低级的类型。因此,它也被称为窄化原始转换(narrowing primitive conversion)。

例如,让我们使用向下强制转换将 long 转换为 short :

long longNum = 24;
short shortNum = (short) longNum;
assertEquals(24, shortNum);

同样地,将一个 double 转换成一个 int:

double doubleNum = 15.6;
int integerNum = (int) doubleNum;
assertEquals(15, integerNum);

但是,我们应该注意到,通过向下强制转换将值过大或过小的高级类型转换为低级类型会导致意外值。

我们将一个long值转换成short类型范围以外的值:

long largeLongNum = 32768; 
short minShortNum = (short) largeLongNum;
assertEquals(-32768, minShortNum);

long smallLongNum = -32769;
short maxShortNum = (short) smallLongNum;
assertEquals(32767, maxShortNum);

如果我们仔细分析转换,就会发现这些不是预期值。

换句话说,当Java在从高级类型转换为低级类型时,达到低级类型的最大值时,下一个值就是低级类型的最小值,反之亦然。

让我们通过例子来理解这一点。当将值为32768的 largeLongNum 转换为short时,shortNum1 的值为-32768。因为 short 的最大值是32767,所以Java会选择 short 的下一个最小值。

类似地,当 smallLongNum 转换为 short 时。shortNum2的值是32767,Java将其作为 short 的下一个最大值。

另外,让我们看看当我们将 long 的最大和最小值转换为 int 时会发生什么:

long maxLong = Long.MAX_VALUE; 
int minInt = (int) maxLong;
assertEquals(-1, minInt);

long minLong = Long.MIN_VALUE;
int maxInt = (int) minLong;
assertEquals(0, maxInt);

4.2. 在包装对象和基本类型之间转换

要将包装对象直接转换为基本类型,我们可以在包装类中使用各种方法,如intValue()shortValue()longValue()。这叫做拆箱。

例如,让我们将Float对象转换为long:

Float floatNum = 17.564f;
long longNum = floatNum.longValue();
assertEquals(17, longNum);

此外,如果我们查看 longValue 或类似方法的实现,我们将发现窄化原始转换的用法:

public long longValue() {
    return (long) value;
}

但是,有时应避免窄化原始转换以保存有价值的信息:

Double doubleNum = 15.9999;
long longNum = doubleNum.longValue();
assertEquals(15, longNum);

转换后,longNum 的值为15。然而,doubleNum 是15.9999,非常接近16。

相反,我们可以使用 Math.round() 转换为最接近的整数:

Double doubleNum = 15.9999;
long longNum = Math.round(doubleNum);
assertEquals(16, longNum);

4.3. 在包装对象之间转换

为此,让我们使用已经讨论过的转换技术。

首先,我们将把包装器对象转换为一个基本值,向下强制转换它并将它转换为另一个包装器对象。换句话说,我们将执行拆箱、向下强制类型转换和装箱技术。

例如,让我们将 Double 对象转换为 Integer 对象:

Double doubleNum = 10.3;
double dbl = doubleNum.doubleValue(); // unboxing
int intgr = (int) dbl; // downcasting
Integer intNum = Integer.valueOf(intgr);
assertEquals(Integer.valueOf(10), intNum);

最后,我们使用Integer. valueof()将基本类型 int 转换为 Integer 对象。这种类型的转换称为装箱。

5. 总结

在本文中,我们通过示例探讨了Java中有损转换的概念。此外,我们还编写了一个方便的列表,其中列出了Java中所有可能的有损转换。

在此过程中,我们已经确定了窄化原始转换是一种简单的方法来转换基本类型并避免有损的转换错误。

同时,我们还探索了Java中用于数值转换的其他方便技术。

本文的代码实现可以在GitHub项目中找到。