Variable Identity
Identity Methods
Java classes support what may be termed identity methods such
as equals
, hashCode
and compareTo
.
The MJ variable classes implement these methods using either MJ identity, which relies on the underlying
value of the variable, or MAPPER identity, which utilizes the displayed value of the variable. The nuances
between MJ identity and MAPPER identity can be a bit esoteric and are described below. However, the default
implementations of equals
, hashCode
and compareTo
are such that an in-depth understanding isn't required to use MJ variables effectively.
MJ Identity: MJString
Example
By default, identity methods of an MJ variable use rules similar to those
employed by MAPPER @IF
, where those rules don't conflict with well-established, Java semantics.
For lack of a better name, call this MJ identity.
For example, consider the MJString
variables of different MAPPER types below. The
equals
, hashCode
and compareTo
methods strip trailing spaces (but
not trailing tabs or leading spaces) before comparing per MAPPER rules. However,
comparison is case-sensitive ala Java practice; Java provides the compareToIgnoreCase
method for case-insensitive comparison, which is also implemented by MJString
.
// @LDV <str1A>a16='abcdef7890ABCDEF' .
MJString str1A = new MJString(VariableScope.LOCAL, MaprptVariableType.ALPHANUMERIC,
16, EnumSet.of(LoadOption.TRUNCATE), "abcdef7890ABCDEF");
assert !str1A.isUsingMapperObjectIdentity();
// @LDV <str2S>s20='Abcdef7890aBCDEF' .
MJString str2S = new MJString(VariableScope.LOCAL, MaprptVariableType.STRING,
20, EnumSet.of(LoadOption.TRUNCATE), "Abcdef7890aBCDEF");
assert !str2S.isUsingMapperObjectIdentity();
// @LDV <str3H>h18='abcdef7890ABCDEF' .
MJString str3H = new MJString(VariableScope.LOCAL, MaprptVariableType.HOLLERITH,
18, EnumSet.of(LoadOption.TRUNCATE), "abcdef7890ABCDEF");
assert !str3H.isUsingMapperObjectIdentity();
// Values are not equal: "abcdef7890ABCDEF".equals("Abcdef7890aBCDEF") == false
assert !str1A.equals(str2S);
assert str2S.compareTo(str1A) != 0;
assert str1A.hashCode() != str2S.hashCode();
// Values are equal: "abcdef7890ABCDEF".equals("abcdef7890ABCDEF") == true
assert str1A.equals(str3H);
assert str3H.compareTo(str1A) == 0;
assert str3H.hashCode() == str1A.hashCode();
MAPPER Identity: MJString
Example
Like other MJ variable classes, MJString
also supports MAPPER identity which
uses the formatted, display value of the variable. When enabled, the equals
,
hashCode
and compareTo
methods
do not strip trailing spaces (or other whitespace). However, comparison is still case-sensitive per Java convention:
str1A.useMapperObjectIdentity(true);
assert str1A.isUsingMapperObjectIdentity();
str2S.useMapperObjectIdentity(true);
assert str2S.isUsingMapperObjectIdentity();
str3H.useMapperObjectIdentity(true);
assert str3H.isUsingMapperObjectIdentity();
// Values still not equal: "abcdef7890ABCDEF".equals("Abcdef7890aBCDEF ") == false
assert !str1A.equals(str2S);
assert str2S.compareTo(str1A) != 0;
assert str1A.hashCode() != str2S.hashCode();
// Values are not equal: "abcdef7890ABCDEF".equals("abcdef7890ABCDEF ") == false
assert !str1A.equals(str3H);
assert str3H.compareTo(str2S) != 0;
assert str3H.hashCode() != str1A.hashCode();
MJ Identity: MJDecimal
Example
Like MJString
, the MJ numeric variable classes support identity methods
that further illustrate the difference between MJ identity and MAPPER identity. The
MJDecimal
variables below contain distinct BigDecimal
values (the basis for MJ identity) but display the same value (the basis for MAPPER identity):
// @LDV <myNum>f3.2=1.23
// See CHG statement below: <mySum> displays 1.67; MAPPER stores <myNum> as 1.23
// @CHG <mySum>f9.2 <myNum> + 0.44 .
//
MJDecimal dec1 = new MJDecimal(VariableScope.LOCAL, 3, 2, EnumSet.noneOf(LoadOption.class), "1.23");
assert !dec1.isUsingMapperObjectIdentity();
assert dec1.decimalValue().unscaledValue().longValue() == 123;
assert dec1.decimalValue().scale() == 2;
// The display value is based on the "MAPPER number" (the raw, BigDecimal value
// 1.23 above is rounded down to fit F3.2 format) and is "1.2".
//
assert ((BigDecimal) dec1.toMapperNumber()).unscaledValue().longValue() == 120;
assert ((BigDecimal) dec1.toMapperNumber()).scale() == 2;
assert "1.2".equals(dec1.formatter().toFormatted());
// @LDV <myNum>f3.2=1.19
// See CHG statement below: <mySum> displays 1.63; MAPPER stores <myNum> as 1.19
// @CHG <mySum>f9.2 <myNum> + 0.44 .
//
MJDecimal dec2 = new MJDecimal(VariableScope.LOCAL, 3, 2, EnumSet.noneOf(LoadOption.class), "1.19");
assert !dec2.isUsingMapperObjectIdentity();
assert dec2.decimalValue().unscaledValue().longValue() == 119;
assert dec2.decimalValue().scale() == 2;
// The display value is based on the "MAPPER number" (the raw, BigDecimal value
// 1.19 above is rounded up to fit F3.2 format) and is "1.2".
//
assert ((BigDecimal) dec2.toMapperNumber()).unscaledValue().longValue() == 120;
assert ((BigDecimal) dec2.toMapperNumber()).scale() == 2;
assert "1.2".equals(dec2.formatter().toFormatted());
// Values are not equal: BigDecimal.valueOf(123,2).equals(BigDecimal.valueOf(119,2)) == false
assert !dec1.equals(dec2);
assert dec2.compareTo(dec1) != 0;
assert dec1.hashCode() != dec2.hashCode();
MAPPER Identity: MJDecimal
Example
Instead of the raw value of the underlying BigDecimal
instance,
MJDecimal
identity methods
utilize the numeric, display value of a variable when MAPPER identity is in effect:
dec1.useMapperObjectIdentity(true);
assert dec1.isUsingMapperObjectIdentity();
dec2.useMapperObjectIdentity(true);
assert dec2.isUsingMapperObjectIdentity();
// Values are equal: BigDecimal.valueOf(12,1).equals(BigDecimal.valueOf(12,1)) == false
assert dec1.equals(dec1);
assert dec2.compareTo(dec1) == 0;
assert dec1.hashCode() == dec2.hashCode();
To summarize, MAPPER identity can be useful in situations where results appear most accurate when variables are collated or organized by their display values.