The Java programming language is a statically typed language, which means that every variable and every expression has a type that is known at compile time. Show
The Java programming language is also a strongly typed language, because types limit the values that a variable (§4.12) can hold or that an expression can produce, limit the operations supported on those values, and determine the meaning of the operations. Strong static typing helps detect errors at compile time. The types of the Java programming language are divided into two categories: primitive types and reference types. The primitive types
(§4.2) are the 4.1. The Kinds of Types and ValuesThere are two kinds of types in the Java programming language: primitive types (§4.2) and reference types (§4.3). There are, correspondingly, two kinds of data values that can be stored in variables, passed as arguments, returned by methods, and operated on: primitive values (§4.2) and reference values (§4.3).
There is also a special null type, the type of the expression Because the null type has no name, it is impossible to declare a variable of the null type or to cast to the null type. The null reference is the only possible value of an expression of null type. The null reference can always undergo a widening reference conversion to any reference type. In practice, the programmer can ignore the null type and just pretend that null is merely a special literal that can be of any reference type. 4.2. Primitive Types and ValuesA primitive type is predefined by the Java programming language and named by its reserved keyword (§3.9): PrimitiveType: NumericType: IntegralType: one of FloatingPointType: one of Primitive values do not share state with other primitive values. The numeric types are the integral types and the floating-point types. The integral types are The floating-point types are The 4.2.1. Integral Types and ValuesThe values of the integral types are integers in the following ranges:
4.2.2. Integer OperationsThe Java programming language provides a number of operators that act on integral values:
Other useful constructors, methods, and constants are predefined in the classes If an integer operator other than a shift operator
has at least one operand of type Otherwise, the operation is carried out using 32-bit precision, and the result of the numerical operator is of type Any value of any integral type may be cast to or from any numeric
type. There are no casts between integral types and the type See §4.2.5 for an idiom to convert integer expressions to The integer operators do not indicate overflow or underflow in any way. An integer operator can throw an exception (§11) for the following reasons:
Example 4.2.2-1. Integer Operations class Test { public static void main(String[] args) { int i = 1000000; System.out.println(i * i); long l = i; System.out.println(l * l); System.out.println(20296 / (l - i)); } } This program produces the output: -727379968 1000000000000 and then encounters an 4.2.3. Floating-Point Types, Formats, and ValuesThe floating-point types are The IEEE 754 standard includes not only positive and negative numbers that consist of a sign and magnitude, but also positive and negative zeros, positive and negative
infinities, and special Not-a-Number values (hereafter abbreviated NaN). A NaN value is used to represent the result of certain invalid operations such as dividing zero by zero. NaN constants of both Every implementation of the Java programming language is required to support two standard sets of floating-point values, called the float value
set and the double value set. In addition, an implementation of the Java programming language may support either or both of two extended-exponent floating-point value sets, called the float-extended-exponent value set and the double-extended-exponent value set. These extended-exponent value sets may, under certain circumstances, be used instead of the standard value sets to represent the values of expressions of type
The finite nonzero values of any floating-point value set can all be expressed in the form s · m · 2(e - N + 1), where s is +1 or -1, m is a positive integer less than 2N, and e is an integer between Emin = -(2K-1-2) and Emax = 2K-1-1, inclusive, and where N and K are parameters that depend on the value set. Some values can be represented in this form in more than one way; for example, supposing that a value v in a value set might be represented in this form using certain values for s, m, and e, then if it happened that m were even and e were less than 2K-1, one could halve m and increase e by 1 to produce a second representation for the same value v. A representation in this form is called normalized if m ≥ 2N-1; otherwise the representation is said to be denormalized. If a value in a value set cannot be represented in such a way that m ≥ 2N-1, then the value is said to be a denormalized value, because it has no normalized representation. The constraints on the parameters N and K (and on the derived parameters Emin and Emax) for the two required and two optional floating-point value sets are summarized in Table 4.1. Table 4.1. Floating-point value set parameters
Where one or both extended-exponent value sets are supported by an implementation, then for each supported extended-exponent value set there is a specific implementation-dependent constant K, whose value is constrained by Table 4.1; this value K in turn dictates the values for Emin and Emax. Each of the four value sets includes not only the finite nonzero values that are ascribed to it above, but also NaN values and the four values positive zero, negative zero, positive infinity, and negative infinity. Note that the constraints in Table 4.1 are designed so that every element of the float value set is necessarily also an element of the float-extended-exponent value set, the double value set, and the double-extended-exponent value set. Likewise, each element of the double value set is necessarily also an element of the double-extended-exponent value set. Each extended-exponent value set has a larger range of exponent values than the corresponding standard value set, but does not have more precision. The elements of the float value set are exactly the values that can be represented using the single floating-point format defined in the IEEE 754 standard. The elements of the double value set are exactly the values that can be represented using the double floating-point format defined in the IEEE 754 standard. Note, however, that the elements of the float-extended-exponent and double-extended-exponent value sets defined here do not correspond to the values that can be represented using IEEE 754 single extended and double extended formats, respectively. The float, float-extended-exponent, double, and double-extended-exponent value sets are not types. It is always correct for an implementation of the Java programming language to use an element of the
float value set to represent a value of type Except for NaN, floating-point values are ordered; arranged from smallest to largest, they are negative infinity, negative finite nonzero values, positive and negative zero, positive finite nonzero values, and positive infinity. IEEE 754 allows multiple distinct NaN values for each of its single and double floating-point formats. While each hardware architecture returns a particular bit pattern for NaN when a new NaN is generated, a programmer can also create NaNs with different bit patterns to encode, for example, retrospective diagnostic information. For the most part, the Java SE platform treats NaN values of a given type as though collapsed into a single canonical value, and hence this specification normally refers to an arbitrary NaN as though to a canonical value. However, version 1.3 of the Java SE platform introduced methods enabling the programmer to
distinguish between NaN values: the Positive zero and negative zero compare equal; thus the result of the expression NaN is unordered, so:
4.2.4. Floating-Point OperationsThe Java programming language provides a number of operators that act on floating-point values:
Other useful constructors, methods, and constants are predefined in the classes If at least one of the operands to a binary operator is of floating-point type, then the operation is a floating-point operation, even if the other is integral. If at least one of the operands to a numerical operator is of type Otherwise, the operation is carried out using 32-bit floating-point arithmetic, and the result of the numerical operator is a value of type Any value of a floating-point type may be cast to or from any numeric type. There are no casts between floating-point types and the type See
§4.2.5 for an idiom to convert floating-point expressions to Operators on floating-point numbers behave as specified by IEEE 754 (with the exception of the remainder operator (§15.17.3)). In particular, the Java programming language requires support of IEEE 754 denormalized floating-point numbers and gradual underflow, which make it easier to prove desirable properties of particular numerical algorithms. Floating-point operations do not "flush to zero" if the calculated result is a denormalized number. The Java programming language requires that floating-point arithmetic behave as if every floating-point operator rounded its floating-point result to the result precision. Inexact results must be rounded to the representable value nearest to the infinitely precise result; if the two nearest representable values are equally near, the one with its least significant bit zero is chosen. This is the IEEE 754 standard's default rounding mode known as round to nearest. The Java programming language uses round toward zero when converting a floating value to an integer (§5.1.3), which acts, in this case, as though the number were truncated, discarding the mantissa bits. Rounding toward zero chooses at its result the format's value closest to and no greater in magnitude than the infinitely precise result. A floating-point operation that overflows produces a signed infinity. A floating-point operation that underflows produces a denormalized value or a signed zero. A floating-point operation that has no mathematically definite result produces NaN. All numeric operations with NaN as an operand produce NaN as a result. A floating-point operator can throw an exception (§11) for the following reasons:
Example 4.2.4-1. Floating-point Operations class Test { public static void main(String[] args) { // An example of overflow: double d = 1e308; System.out.print("overflow produces infinity: "); System.out.println(d + "*10==" + d*10); // An example of gradual underflow: d = 1e-305 * Math.PI; System.out.print("gradual underflow: " + d + "\n "); for (int i = 0; i < 4; i++) System.out.print(" " + (d /= 100000)); System.out.println(); // An example of NaN: System.out.print("0.0/0.0 is Not-a-Number: "); d = 0.0/0.0; System.out.println(d); // An example of inexact results and rounding: System.out.print("inexact results with float:"); for (int i = 0; i < 100; i++) { float z = 1.0f / i; if (z * i != 1.0f) System.out.print(" " + i); } System.out.println(); // Another example of inexact results and rounding: System.out.print("inexact results with double:"); for (int i = 0; i < 100; i++) { double z = 1.0 / i; if (z * i != 1.0) System.out.print(" " + i); } System.out.println(); // An example of cast to integer rounding: System.out.print("cast to int rounds toward 0: "); d = 12345.6; System.out.println((int)d + " " + (int)(-d)); } } This program produces the output: overflow produces infinity: 1.0e+308*10==Infinity gradual underflow: 3.141592653589793E-305 3.1415926535898E-310 3.141592653E-315 3.142E-320 0.0 0.0/0.0 is Not-a-Number: NaN inexact results with float: 0 41 47 55 61 82 83 94 97 inexact results with double: 0 49 98 cast to int rounds toward 0: 12345 -12345 This example demonstrates, among other things, that gradual underflow can result in a gradual loss of precision. The
results when 4.2.5. The boolean Type and boolean ValuesThe The boolean operators are:
Boolean expressions determine the control flow in several kinds of statements:
A Only An integer or floating-point expression An object reference A A cast of a 4.3. Reference Types and ValuesThere are four kinds of reference types: class types (§8), interface types (§9), type variables (§4.4), and array types (§10). ReferenceType: ClassOrInterfaceType: ClassType: InterfaceType: TypeDeclSpecifier: TypeName: TypeVariable: ArrayType: The sample code: class Point { int[] metrics; } interface Move { void move(int deltax, int deltay); } declares a class type A class or interface type consists of a type declaration specifier, optionally followed by type arguments (§4.5.1). If type arguments appear anywhere in a class or interface type, it is a parameterized type (§4.5). A type declaration specifier may be either a type name
(§6.5.5), or a class or interface type followed by "." and an identifier. In the latter case, the specifier has the form There are contexts in the Java programming language where a generic class or interface name is used without providing type arguments. Such contexts do not involve the use of raw types (§4.8). Rather, they are contexts where type arguments are unnecessary for, or irrelevant to, the meaning of the generic class or interface. For example, a single-type-import declaration Because of the occasional need to use a generic class or interface name without type arguments, type names are distinct from type declaration specifiers. A type name is always qualified by means of another type name. In some cases, this is necessary to access an inner class that is a member of a parameterized type. Here is an example of where a type declaration specifier is distinct from a type name: class GenericOuter<T extends Number> { public class Inner<S extends Comparable<S>> { T getT() { return null;} S getS() { return null;} } } class Test { public static void main(String[] args) { GenericOuter<Integer>.Inner<Double> x1 = null; Integer i = x1.getT(); Double d = x1.getS(); } } If we accessed GenericOuter.Inner x2 = null; we would force its use as a raw type, losing type information. 4.3.1. ObjectsAn object is a class instance or an array. The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object. A class instance is explicitly created by a class instance creation expression (§15.9). An array is explicitly created by an array creation expression (§15.10). A new class instance is implicitly created when the string concatenation operator A new array object is implicitly created when an array initializer expression (§10.6) is evaluated; this can occur when a class or interface is initialized (§12.4), when a new instance of a class is created (§15.9), or when a local variable declaration statement is executed (§14.4). New objects of the types Example 4.3.1-1. Object Creation class Point { int x, y; Point() { System.out.println("default"); } Point(int x, int y) { this.x = x; this.y = y; } /* A Point instance is explicitly created at class initialization time: */ static Point origin = new Point(0,0); /* A String can be implicitly created by a + operator: */ public String toString() { return "(" + x + "," + y + ")"; } } class Test { public static void main(String[] args) { /* A Point is explicitly created using newInstance: */ Point p = null; try { p = (Point)Class.forName("Point").newInstance(); } catch (Exception e) { System.out.println(e); } /* An array is implicitly created by an array constructor: */ Point a[] = { new Point(0,0), new Point(1,1) }; /* Strings are implicitly created by + operators: */ System.out.println("p: " + p); System.out.println("a: { " + a[0] + ", " + a[1] + " }"); /* An array is explicitly created by an array creation expression: */ String sa[] = new String[2]; sa[0] = "he"; sa[1] = "llo"; System.out.println(sa[0] + sa[1]); } } This program produces the output: default p: (0,0) a: { (0,0), (1,1) } hello The operators on references to objects are:
There may be many references to the same object. Most objects have state, stored in the fields of objects that are instances of classes or in the variables that are the components of an array object. If two variables contain references to the same object, the state of the object can be modified using one variable's reference to the object, and then the altered state can be observed through the reference in the other variable. Example 4.3.1-2. Primitive and Reference Identity class Value { int val; } class Test { public static void main(String[] args) { int i1 = 3; int i2 = i1; i2 = 4; System.out.print("i1==" + i1); System.out.println(" but i2==" + i2); Value v1 = new Value(); v1.val = 5; Value v2 = v1; v2.val = 6; System.out.print("v1.val==" + v1.val); System.out.println(" and v2.val==" + v2.val); } } This program produces the output: i1==3 but i2==4 v1.val==6 and v2.val==6 because Each object is associated with a monitor
(§17.1), which is used by 4.3.2. The Class ObjectThe class All class and array types inherit (§8.4.8) the methods of class
4.3.3. The Class StringInstances of class A String literals (§3.10.5) are references to instances of class The string concatenation operator
4.3.4. When Reference Types Are the SameTwo reference types are the same compile-time type if they have the same binary name (§13.1) and their type arguments, if any, are the same, applying this definition recursively. When two reference types are the same, they are sometimes said to be the same class or the same interface. At run time, several reference types with the same binary name may be loaded simultaneously by different class loaders. These types may or may not represent the same type declaration. Even if two such types do represent the same type declaration, they are considered distinct. Two reference types are the same run-time type if:
4.4. Type VariablesA type variable is an unqualified identifier used as a type in class, interface, method, and constructor bodies. A type variable is declared as a type parameter of a generic class declaration (§8.1.2), generic interface declaration (§9.1.2), generic method declaration (§8.4.4), or generic constructor declaration (§8.8.4). TypeParameter: TypeBound: AdditionalBoundList: AdditionalBound: The scope of a type variable declared as a type parameter is specified in §6.3. Every type variable declared as a type parameter has a bound. If no
bound is declared for a type variable,
It is a compile-time error if any of the types I1 ... In is a class type or type variable. The erasures (§4.6) of all constituent types of a bound must be pairwise different, or a compile-time error occurs. A type variable must not at the same time be a subtype of two interface types which are different parameterizations of the same generic interface, or a compile-time error occurs. The order of types in a bound is only significant in that the erasure of a type variable is determined by the first type in its bound, and that a class type or type variable may only appear in the first position. The members of a type variable X with bound T Example 4.4-1. Members of a Type Variable package TypeVarMembers; class C { public void mCPublic() {} protected void mCProtected() {} void mCDefault() {} private void mCPrivate() {} } interface I { void mI(); } class CT extends C implements I { public void mI() {} } class Test { <T extends C & I> void test(T t) { t.mI(); // OK t.mCPublic(); // OK t.mCProtected(); // OK t.mCDefault(); // OK t.mCPrivate(); // Compile-time error } } The type variable If 4.5. Parameterized TypesA generic class or interface declaration C (§8.1.2, §9.1.2) with one or more type parameters A1,...,An which have corresponding bounds B1,...,Bn defines a set of parameterized types, one for each possible invocation of the type parameter section. Each parameterized type in the set is of the form C A parameterized type is written as a ClassType or InterfaceType that contains at least one type declaration specifier immediately followed by a type argument list Given a type declaration specifier immediately followed by a type argument list, let C be the final Identifier in the specifier. It is a compile-time error if C is not the name of a generic class or interface, or if the number of type arguments in the type argument list differs from the number of type parameters of C. Let P = C The notation In this specification, whenever we speak of a class or interface type, we include the generic version as well, unless explicitly excluded. Examples of parameterized types:
Examples of incorrect invocations of a generic type:
A parameterized type may be an invocation of a generic class or interface
which is nested. For example, if a non-generic class C has a generic member class D Two parameterized types are provably distinct if either of the following conditions hold:
4.5.1. Type Arguments and WildcardsType arguments may be either reference types or wildcards. Wildcards are useful in situations where only partial knowledge about the type parameter is required. TypeArguments: TypeArgumentList: TypeArgument: Wildcard: WildcardBounds: Example 4.5.1-1. Wildcards import java.util.Collection; import java.util.ArrayList; class Test { static void printCollection(Collection<?> c) { // a wildcard collection for (Object o : c) { System.out.println(o); } } public static void main(String[] args) { Collection<String> cs = new ArrayList<String>(); cs.add("hello"); cs.add("world"); printCollection(cs); } } Note that using Here is an example where the element type of an array is parameterized by a wildcard: public Method getMethod(Class<?>[] parameterTypes) { ... } Wildcards may be given explicit bounds, just like regular type variable declarations. An upper bound is signified by the following syntax, where B is the bound: ? extends B Unlike ordinary type variables declared in a method signature, no type inference is required when using a wildcard. Consequently, it is permissible to declare lower bounds on a wildcard, using the following syntax, where B is a lower bound: ? super B Example 4.5.1-2. Bounded Wildcards boolean addAll(Collection<? extends E> c) Here, the method is declared within the interface <T> boolean addAll(Collection<T> c) This version is sufficiently flexible, but note that the type parameter is used only once in the signature. This reflects the fact that the type parameter is not being used to express any kind of interdependency between the type(s) of the argument(s), the return type and/or throws type. In the absence of such interdependency, generic methods are considered bad style, and wildcards are preferred. Reference(T referent, ReferenceQueue<? super T> queue); Here, the referent can be inserted into any queue whose element type is a supertype of the type Two type arguments are provably distinct if one of the following is true:
A type argument T1 is said to contain another type argument T2, written T2
The relationship of wildcards to established type theory is an interesting one, which we briefly allude to here. Wildcards are a restricted form of existential types. Given a generic type declaration G Historically, wildcards are a direct descendant of the work by Atsushi Igarashi and Mirko Viroli. Readers interested in a more comprehensive discussion should refer to On Variance-Based Subtyping for Parametric Types by Atsushi Igarashi and Mirko Viroli, in the Proceedings of the 16th European Conference on Object Oriented Programming (ECOOP 2002). This work itself builds upon earlier work by Kresten Thorup and Mads Torgersen (Unifying Genericity, ECOOP 99), as well as a long tradition of work on declaration based variance that goes back to Pierre America's work on POOL (OOPSLA 89). Wildcards differ in certain details from the constructs described in the aforementioned paper, in particular in the use of capture conversion (§5.1.10) rather than the 4.5.2. Members and Constructors of Parameterized TypesLet C be a generic
class or interface declaration with type parameters A1,...,An, and let C
If any of the type arguments in the invocation of C are wildcards, then:
This is of no consequence, as it is impossible to access a member of a parameterized type without performing capture conversion
(§5.1.10), and it is impossible to use a wildcard type after the keyword The sole exception to the previous paragraph is when a nested parameterized type is used as the expression in an 4.6. Type ErasureType erasure is a mapping from types (possibly including parameterized types and type variables) to types (that are never parameterized types or type variables). We write |T| for the erasure of type T. The erasure mapping is defined as follows:
Type erasure also maps the signature (§8.4.2) of a constructor or method to a signature that has no parameterized types or type variables. The erasure of a constructor or method signature s is a signature consisting of the same name as s and the erasures of all the formal parameter types given in s. The type parameters of a constructor or method (§8.4.4), and the return type (§8.4.5) of a method, also undergo erasure if the constructor or method's signature is erased. The erasure of the signature of a generic method has no type parameters. 4.7. Reifiable TypesBecause some type information is erased during compilation, not all types are available at run time. Types that are completely available at run time are known as reifiable types. A type is reifiable if and only if one of the following holds:
An intersection type is not reifiable. The decision not to make all generic types reifiable is one of the most crucial, and controversial design decisions involving the type system of the Java programming language. Ultimately, the most important motivation for this decision is compatibility with existing code. In a naive sense, the addition of new constructs such as generics has no implications for pre-existing code. The Java programming language, per se, is compatible with earlier versions as long as every program written in the
previous versions retains its meaning in the new version. However, this notion, which may be termed language compatibility, is of purely theoretical interest. Real programs (even trivial ones, such as "Hello World") are composed of several compilation units, some of which are provided by the Java SE platform (such as elements of One way to provide platform compatibility is to leave existing platform functionality unchanged, only adding new functionality. For example, rather than modify the existing Collections hierarchy in The disadvantages of such a scheme is that it is extremely difficult for pre-existing clients of the Collection library to migrate to the new library. Collections are used to exchange data between independently developed modules; if a vendor decides to switch to the new, generic, library, that vendor must also distribute two versions of their code, to be compatible with their clients. Libraries that are dependent on other vendors code cannot be modified to use generics until the supplier's library is updated. If two modules are mutually dependent, the changes must be made simultaneously. Clearly, platform compatibility, as outlined above, does not provide a realistic path for adoption of a pervasive new feature such as generics. Therefore, the design of the generic type system seeks to support migration compatibility. Migration compatibiliy allows the evolution of existing code to take advantage of generics without imposing dependencies between independently developed software modules. The price of migration compatibility is that a full and sound reification of the generic type system is not possible, at least while the migration is taking place. 4.8. Raw TypesTo facilitate interfacing with non-generic legacy code, it is possible to use as a type the erasure (§4.6) of a parameterized type (§4.5) or the erasure of an array type (§10.1) whose element type is a parameterized type. Such a type is called a raw type. More precisely, a raw type is defined to be one of:
A non-generic class or interface type is not a raw type. To see why a non- class Outer<T>{ T t; class Inner { T setOuterT(T t1) { t = t1; return t; } } } The type of the member(s) of This rule applies only to type members that are not inherited. Inherited type members that depend on type variables will be inherited as raw types as a consequence of the rule that the supertypes of a raw type are erased, described later in this section. Another implication of the rules above is that a generic inner class of a raw type can itself only be used as a raw type: class Outer<T>{ class Inner<S> { S s; } } It is not possible to access Outer.Inner<Double> x = null; // illegal Double d = x.s; because The superclasses (respectively, superinterfaces) of a raw type are the erasures of the superclasses (superinterfaces) of any of its parameterized invocations. The type of a constructor (§8.8), instance method
(§8.4, §9.4), or non-static field (§8.3) The type of a static method or static field of a raw type C is the same as its type in the generic declaration corresponding to C. It is a compile-time error to pass type arguments to a non- It is a compile-time error to attempt to use a type member of a parameterized type as a raw type. This means that the ban on "rare" types extends to the case where the qualifying type is parameterized, but we attempt to use the inner class as a raw type: Outer<Integer>.Inner x = null; // illegal This is the opposite of the case discussed above. There is no practical justification for this half-baked type. In legacy code, no type arguments are used. In non-legacy code, we should use the generic types correctly and pass all the required type arguments. The supertype of a class may be a raw type. Member accesses for the class are treated as normal, and member accesses for the supertype are treated as for raw types. In the constructor of the class, calls to The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of generics into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types. To make sure that potential violations of the typing rules are always flagged, some accesses to members of a raw type will result in compile-time unchecked warnings. The rules for compile-time unchecked warnings when accessing members or constructors of raw types are as follows:
Note that the unchecked warnings above are distinct from the unchecked warnings possible from unchecked conversion (§5.1.9), casts (§5.5.2), method declarations (§8.4.1, §8.4.8.3, §8.4.8.4, §9.4.1.2), and variable arity method invocations (§15.12.4.2). The warnings here cover the case where a legacy consumer uses a generified library. For example, the library declares a generic class (Note that the legacy consumer can assign a The warnings from unchecked conversion cover the dual case, where a generified consumer uses a legacy library. For example, a method of the library has the raw return type
Example 4.8-1. Raw Types class Cell<E> { E value; Cell(E v) { value = v; } E get() { return value; } void set(E v) { value = v; } public static void main(String[] args) { Cell x = new Cell<String>("abc"); System.out.println(x.value); // OK, has type Object System.out.println(x.get()); // OK, has type Object x.set("def"); // unchecked warning } } Example 4.8-2. Raw Types and Inheritance import java.util.*; class NonGeneric { Collection<Number> myNumbers() { return null; } } abstract class RawMembers<T> extends NonGeneric implements Collection<String> { static Collection<NonGeneric> cng = new ArrayList<NonGeneric>(); public static void main(String[] args) { RawMembers rw = null; Collection<Number> cn = rw.myNumbers(); // OK Iterator<String> is = rw.iterator(); // Unchecked warning Collection<NonGeneric> cnn = rw.cng; // OK, static member } } In this program, Iterator<String> iterator() from the As a result, the attempt to assign to In contrast, the static member Raw types are closely related to wildcards. Both are based on existential types. Raw types can be thought of as wildcards whose type rules are deliberately unsound, to accommodate interaction with legacy code. Historically, raw types preceded wildcards; they were first introduced in GJ, and described in the paper Making the future safe for the past: Adding Genericity to the Java Programming Language by Gilad Bracha, Martin Odersky, David Stoutamire, and Philip Wadler, in Proceedings of the ACM Conference on Object-Oriented Programming, Systems, Languages and Applications (OOPSLA 98), October 1998. 4.9. Intersection TypesAn intersection type takes the form T1 Intersection types arise in the processes of capture conversion (§5.1.10) and type inference (§15.12.2.7). It is not possible to write an intersection type directly as part of a program; no syntax supports this. The values of an intersection type are those objects that are values of all of the types Ti for 1 ≤ i ≤ n. The members of an intersection type T1
It is worth dwelling upon the distinction between intersection types and the bounds of type variables. Every type variable bound induces an intersection type. This intersection type is often trivial (i.e., consists of a single type). The form of a bound is restricted (only the first element may be a class or type variable, and only one type variable may appear in the bound) to preclude certain awkward situations coming into existence. However, capture conversion can lead to the creation of type variables whose bounds are more general (e.g., array types). 4.10. SubtypingThe subtype and supertype relations are binary relations on types. The
supertypes of a type are obtained by reflexive and transitive closure over the direct supertype relation, written S S is a proper supertype of T, written
S The subtypes of a type T are all types U such that T is a supertype of U, and the null type. We write T T is a proper subtype of S, written T T is a direct subtype of S, written T Subtyping does not extend through
parameterized types: T 4.10.1. Subtyping among Primitive TypesThe following rules define the direct supertype relation among the primitive types:
4.10.2. Subtyping among Class and Interface TypesGiven a generic type declaration C
The direct supertypes of the parameterized type
C
The direct supertypes of the parameterized type C The direct supertypes of an intersection type T1
The direct supertypes of a type variable are the types listed in its bound. A type variable is a direct supertype of its lower bound. The direct supertypes of the null type are all reference types other than the null type itself. 4.10.3. Subtyping among Array TypesThe following rules define the direct supertype relation among array types:
4.11. Where Types Are UsedTypes are used when they appear in declarations or in certain expressions. Example 4.11-1. Usage of a Type import java.util.Random; import java.util.Collection; import java.util.ArrayList; class MiscMath<T extends Number> { int divisor; MiscMath(int divisor) { this.divisor = divisor; } float ratio(long l) { try { l /= divisor; } catch (Exception e) { if (e instanceof ArithmeticException) l = Long.MAX_VALUE; else l = 0; } return (float)l; } double gausser() { Random r = new Random(); double[] val = new double[2]; val[0] = r.nextGaussian(); val[1] = r.nextGaussian(); return (val[0] + val[1]) / 2; } Collection<Number> fromArray(Number[] na) { Collection<Number> cn = new ArrayList<Number>(); for (Number n : na) cn.add(n); return cn; } <S> void loop(S s) { this.<S>loop(s); } } In this example, types are used in declarations of the following:
and in expressions of the following kinds:
4.12. VariablesA variable is a storage location and has an associated type, sometimes called its compile-time type, that is either a primitive type (§4.2) or a reference type (§4.3). A variable's value is changed by an
assignment (§15.26) or by a prefix or postfix Compatibility of the value of a variable with its type is guaranteed by the design of the Java programming language, as long as a program does not give rise to compile-time unchecked warnings (§4.12.2). Default values (§4.12.5) are compatible and all assignments to a variable are checked for assignment compatibility (§5.2), usually at compile time, but, in a single case involving arrays, a run-time check is made (§10.5). 4.12.1. Variables of Primitive TypeA variable of a primitive type always holds a primitive value of that exact primitive type. 4.12.2. Variables of Reference TypeA variable of a class type T can hold a null reference or a reference to an instance of class T or of any class that is a subclass of T. A variable of an interface type can hold a null reference or a reference to any instance of any class that implements the interface. Note that a variable is not guaranteed to always refer to a subtype of its declared type, but only to subclasses or subinterfaces of the declared type. This is due to the possibility of heap pollution discussed below. If T is a primitive type, then a variable of type "array of T" can hold a null reference or a reference to any array of type "array of T". If T is a reference type, then a variable of type "array of T" can hold a null reference or a reference to any array of type "array of S" such that type S is a subclass or subinterface of type T. A variable of type A variable of type It is possible that a variable of a parameterized type will refer to an object that is not of that parameterized type. This situation is known as heap pollution. Heap pollution can only occur if the program performed some operation involving a raw type that would give rise to a compile-time unchecked warning (§4.8, §5.1.9, §5.5.2, §8.4.1, §8.4.8.3, §8.4.8.4, §9.4.1.2, §15.12.4.2), or if the program aliases an array variable of non-reifiable element type through an array variable of a supertype which is either raw or non-generic. For example, the code: List l = new ArrayList<Number>(); List<String> ls = l; // Unchecked warning gives rise to a compile-time unchecked warning, because it is not possible to ascertain, either at compile time (within the limits of the compile-time type checking rules) or at run time, whether the variable If the code above is executed, heap
pollution arises, as the variable The problem cannot be identified at run time because type variables are not reified, and thus instances do not carry any information at run time regarding the type arguments used to create them. In a simple example as given above, it may appear that it should be straightforward to identify the situation at compile time and give an error. However, in the general
(and typical) case, the value of the variable Furthermore, the fact that static void m(List<String>... stringLists) { Object[] array = stringLists; List<Integer> tmpList = Arrays.asList(42); array[0] = tmpList; // (1) String s = stringLists[0].get(0); // (2) } Heap
pollution occurs at (1) because a component in the A compile-time unchecked warning will be given at any invocation of the
method above because an invocation is considered by the Java programming language's static type system to create an array whose element type, Finally, note that the The variable will always refer to an object that is an instance of a class that represents the parameterized type. The value of Assignment from an expression of a raw type to a variable of a parameterized type should only be used when combining legacy code which does not make use of parameterized types with more modern code that does. If no operation that requires a compile-time unchecked warning to be issued takes place, and no unsafe aliasing occurs of array variables with non-reifiable element types, then heap pollution cannot occur. Note that this does not imply that heap pollution only occurs if a compile-time unchecked warning actually occurred. It is possible to run a program where some of the binaries were produced by a compiler for an older version of the Java programming language, or from sources that explicitly suppressed unchecked warnings. This practice is unhealthy at best. Conversely, it is possible that despite executing code that could (and perhaps did) give rise to a compile-time unchecked warning, no heap pollution takes place. Indeed, good programming practice requires that the programmer satisfy herself that despite any unchecked warning, the code is correct and heap pollution will not occur. 4.12.3. Kinds of VariablesThere are seven kinds of variables:
Example 4.12.3-1. Different Kinds of Variables class Point { static int numPoints; // numPoints is a class variable int x, y; // x and y are instance variables int[] w = new int[10]; // w[0] is an array component int setX(int x) { // x is a method parameter int oldx = this.x; // oldx is a local variable this.x = x; return oldx; } } 4.12.4. final VariablesA variable can be declared It is a compile-time error if a A blank Once a This applies also to arrays, because arrays are objects; if a Example 4.12.4-1. Final Variables class Point { int x, y; int useCount; Point(int x, int y) { this.x = x; this.y = y; } static final Point origin = new Point(0, 0); } In this program, the class
A variable of primitive type or type Whether a variable is a constant variable or not may have implications with respect to class initialization (§12.4.1), binary compatibility (§13.1, §13.4.9) and definite assignment (§16). A resource of a An exception parameter of a uni- 4.12.5. Initial Values of VariablesEvery variable in a program must have a value before its value is used:
Example 4.12.5-1. Initial Values of Variables class Point { static int npoints; int x, y; Point root; } class Test { public static void main(String[] args) { System.out.println("npoints=" + Point.npoints); Point p = new Point(); System.out.println("p.x=" + p.x + ", p.y=" + p.y); System.out.println("p.root=" + p.root); } } This program prints: npoints=0 p.x=0, p.y=0 p.root=null illustrating
the default initialization of 4.12.6. Types, Classes, and InterfacesIn the Java programming language, every variable and every expression has a type that can be determined at compile time. The type may be a primitive type or a reference type. Reference types include class types and interface types. Reference types are introduced by type declarations, which include class declarations (§8.1) and interface declarations (§9.1). We often use the term type to refer to either a class or an interface. In the Java Virtual Machine, every object belongs to some
particular class: the class that was mentioned in the creation expression that produced the object (§15.9), or the class whose Every array also has a class. The method The compile-time type of a variable is always declared, and the compile-time type of an expression can be deduced at compile time. The compile-time type limits the possible values that the variable can hold at run time or the expression can
produce at run time. If a run-time value is a reference that is not Even though a variable or expression may have a compile-time type that is an interface type, there are no instances of interfaces. A variable or expression whose type is an interface type can reference any object whose class implements (§8.1.5) that interface. Sometimes a variable or expression is said to have a "run-time type". This refers to the class of the object referred to by the value of the variable or expression at run time, assuming that the value is not The correspondence between compile-time types and run-time types is incomplete for two reasons:
Example 4.12.6-1. Type of a Variable versus Class of an Object interface Colorable { void setColor(byte r, byte g, byte b); } class Point { int x, y; } class ColoredPoint extends Point implements Colorable { byte r, g, b; public void setColor(byte rv, byte gv, byte bv) { r = rv; g = gv; b = bv; } } class Test { public static void main(String[] args) { Point p = new Point(); ColoredPoint cp = new ColoredPoint(); p = cp; Colorable c = cp; } } In this example:
Note that an expression such as Which of the following is typically used in a flowchart to indicate a decisions?Flowcharts typically use the following main symbols: A process step, usually called an activity, is denoted as a rectangular box. A decision is usually denoted as a diamond.
When you code an if statement within another if Statement The statements are quizlet?Terms in this set (20) When you code an if statement within another if statement, the statements are nested.
Which of the following data types can store the value 0 using the least amount of memory?The data type that can store the value 0 using least amount of memory is “ Byte ”.
When you create a StringBuilder object with an initial value of Juan Its capacity is 16?When you create a StringBuilder object with an initial value of "Juan", its capacity is the length of the String contained in StringBuilder, 4, plus 16 more, for a total of 20.
|