Wildcard (Java)

Last updated

The wildcard? in Java is a special kind of type argument [1] that controls the type safety of the use of generic (parameterized) types. It can be used in variable declarations and instantiations as well as in method definitions, but not in the definition of a generic type. [2] [3] This is a form of use-site variance annotation, in contrast with the definition-site variance annotations found in C# and Scala.

Contents

Covariance for generic types

Unlike arrays (which are covariant in Java), different instantiations of a generic type are not compatible with each other, not even explicitly: With the declaration Generic<Supertype> superGeneric; Generic<Subtype> subGeneric; the compiler would report a conversion error for both castings (Generic<Subtype>)superGeneric and (Generic<Supertype>)subGeneric.

This incompatibility may be softened by the wildcard if ? is used as an actual type parameter: Generic<?> is a supertype of all parameterizarions of the generic type Generic. This allows objects of type Generic<Supertype> and Generic<Subtype> to be safely assigned to a variable or method parameter of type Generic<?>. Using Generic<? extends Supertype> allows the same, restricting compatibility to Supertype and its children. Another possibility is Generic<? super Subtype>, which also accepts both objects and restricts compatibility to Subtype and all its parents.

Wildcard as parameter type

In the body of a generic unit, the (formal) type parameter is handled like its upper bound (expressed with extends; Object if not constrained). If the return type of a method is the type parameter, the result (e.g. of type ?) can be referenced by a variable of the type of the upper bound (or Object). In the other direction, the wildcard fits no other type, not even Object: If ? has been applied as the formal type parameter of a method, no actual parameters can be passed to it. However, objects of the unknown type can be read from the generic object and assigned to a variable of a supertype of the upperbound.

classGeneric<TextendsUpperBound>{privateTt;voidwrite(Tt){this.t=t;}Tread(){returnt;}}...Generic<UpperBound>concreteTypeReference=newGeneric<UpperBound>();Generic<?>wildcardReference=concreteTypeReference;UpperBoundub=wildcardReference.read();// Object would also be OKwildcardReference.write(newObject());// type errorwildcardReference.write(newUpperBound());// type errorconcreteTypeReference.write(newUpperBound());// OK

Bounded wildcards

A bounded wildcard is one with either an upper or a lower inheritance constraint. The bound of a wildcard can be either a class type, interface type, array type, or type variable. Upper bounds are expressed using the extends keyword and lower bounds using the super keyword. Wildcards can state either an upper bound or a lower bound, but not both.

Upper bounds

An upper bound on a wildcard must be a subtype of the upper bound of the corresponding type parameter declared in the corresponding generic type. An example of a wildcard that explicitly states an upper bound is:

Generic<? extends SubtypeOfUpperBound> referenceConstrainedFromAbove;

This reference can hold any parameterization of Generic whose type argument is a subtype of SubtypeOfUpperBound. A wildcard that does not explicitly state an upper bound is effectively the same as one that has the constraint extends Object, since all reference types in Java are subtypes of Object.

Lower bounds

A wildcard with a lower bound, such as

Generic<? super SubtypeOfUpperBound> referenceConstrainedFromBelow;

can hold any parameterization of Generic whose any type argument is both a subtype of the corresponding type parameter's upper bound and a supertype of SubtypeOfUpperBound.

Object creation with wildcard

No objects may be created with a wildcard type argument: for example, new Generic<?>() is forbidden. In practice, this is unnecessary because if one wanted to create an object that was assignable to a variable of type Generic<?>, one could simply use any arbitrary type (that falls within the constraints of the wildcard, if any) as the type argument.

However, new ArrayList<Generic<?>>() is allowed, because the wildcard is not a parameter to the instantiated type ArrayList. The same holds for new ArrayList<List<?>>().

In an array creation expression, the component type of the array must be reifiable as defined by the Java Language Specification, Section 4.7. This entails that, if the component type of the array has any type arguments, they must all be unbounded wildcards (wildcards consisting of only a ?) . For example, new Generic<?>[20] is correct, while new Generic<SomeType>[20] is not.

For both cases, using no parameters is another option. This will generate a warning since it is less type-safe (see Raw type).

Example: Lists

In the Java Collections Framework, the class List<MyClass> represents an ordered collection of objects of type MyClass. Upper bounds are specified using extends: A List<? extends MyClass> is a list of objects of some subclass of MyClass, i.e. any object in the list is guaranteed to be of type MyClass, so one can iterate over it using a variable of type MyClass [4]

publicvoiddoSomething(List<?extendsMyClass>list){for(MyClassobject:list){// OK// do something}}

However, it is not guaranteed that one can add any object of type MyClass to that list:

publicvoiddoSomething(List<?extendsMyClass>list){MyClassm=newMyClass();list.add(m);// Compile error}

The converse is true for lower bounds, which are specified using super: A List<? super MyClass> is a list of objects of some superclass of MyClass, i.e. the list is guaranteed to be able to contain any object of type MyClass, so one can add any object of type MyClass:

publicvoiddoSomething(List<?superMyClass>list){MyClassm=newMyClass();list.add(m);// OK}

However, it is not guaranteed that one can iterate over that list using a variable of type MyClass:

publicvoiddoSomething(List<?superMyClass>list){for(MyClassobject:list){// Compile error// do something}}

In order to be able to do both add objects of type MyClass to the list and iterate over it using a variable of type MyClass, a List<MyClass> is needed, which is the only type of List that is both List<? extends MyClass> and List<? super MyClass>. [5]

The mnemonics PECS (Producer Extends, Consumer Super) from the book Effective Java by Joshua Bloch gives an easy way to remember when to use wildcards (corresponding to Covariance and Contravariance) in Java.

See also

Related Research Articles

Eiffel is an object-oriented programming language designed by Bertrand Meyer and Eiffel Software. Meyer conceived the language in 1985 with the goal of increasing the reliability of commercial software development; the first version becoming available in 1986. In 2005, Eiffel became an ISO-standardized language.

Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters. This approach, pioneered by the ML programming language in 1973, permits writing common functions or types that differ only in the set of types on which they operate when used, thus reducing duplication. Such software entities are known as generics in Ada, C#, Delphi, Eiffel, F#, Java, Nim, Python, Rust, Swift, TypeScript and Visual Basic .NET. They are known as parametric polymorphism in ML, Scala, Julia, and Haskell ; templates in C++ and D; and parameterized types in the influential 1994 book Design Patterns.

In programming language theory, subtyping is a form of type polymorphism in which a subtype is a datatype that is related to another datatype by some notion of substitutability, meaning that program elements, typically subroutines or functions, written to operate on elements of the supertype can also operate on elements of the subtype. If S is a subtype of T, the subtyping relation is often written S <: T, to mean that any term of type S can be safely used in a context where a term of type T is expected. The precise semantics of subtyping crucially depends on the particulars of what "safely used in a context where" means in a given programming language. The type system of a programming language essentially defines its own subtyping relation, which may well be trivial, should the language support no conversion mechanisms.

In programming languages and type theory, polymorphism is the provision of a single interface to entities of different types or the use of a single symbol to represent multiple different types.The concept is borrowed from a principle in biology where an organism or species can have many different forms or stages.

In computer science, a type signature or type annotation defines the inputs and outputs for a function, subroutine or method. A type signature includes the number, types and order of the arguments contained by a function. A type signature is typically used during overload resolution for choosing the correct definition of a function to be called among many overloaded forms.

In knowledge representation, object-oriented programming and design, is-a is a subsumption relationship between abstractions, wherein one class A is a subclass of another class B . In other words, type A is a subtype of type B when A's specification implies B's specification. That is, any object that satisfies A's specification also satisfies B's specification, because B's specification is weaker.

In computer programming, a function object is a construct allowing an object to be invoked or called as if it were an ordinary function, usually with the same syntax. Function objects are often called functors.

This article compares two programming languages: C# with Java. While the focus of this article is mainly the languages and their features, such a comparison will necessarily also consider some features of platforms and libraries. For a more detailed comparison of the platforms, see Comparison of the Java and .NET platforms.

In class-based object-oriented programming, a constructor is a special type of subroutine called to create an object. It prepares the new object for use, often accepting arguments that the constructor uses to set required member variables.

Java syntax

The syntax of Java refers to the set of rules defining how a Java program is written and interpreted.

Many programming language type systems support subtyping. For instance, if the type Cat is a subtype of Animal, then an expression of type Cat should be substitutable wherever an expression of type Animal is used.

In mathematical logic and computer science, some type theories and type systems include a top type that is commonly denoted with top or the symbol ⊤. The top type is sometimes called also universal type, or universal supertype as all other types in the type system of interest are subtypes of it, and in most cases, it contains every possible object of the type system. It is in contrast with the bottom type, or the universal subtype, which every other type is supertype of and it is often that the type contains no members at all.

In computer programming, an anonymous function is a function definition that is not bound to an identifier. Anonymous functions are often arguments being passed to higher-order functions or used for constructing the result of a higher-order function that needs to return a function. If the function is only used once, or a limited number of times, an anonymous function may be syntactically lighter than using a named function. Anonymous functions are ubiquitous in functional programming languages and other languages with first-class functions, where they fulfil the same role for the function type as literals do for other data types.

Generics are a facility of generic programming that were added to the Java programming language in 2004 within version J2SE 5.0. They were designed to extend Java's type system to allow "a type or method to operate on objects of various types while providing compile-time type safety". The aspect compile-time type safety was not fully achieved, since it was shown in 2016 that it is not guaranteed in all cases.

clone is a method in the Java programming language for object duplication. In Java, objects are manipulated through reference variables, and there is no operator for copying an object—the assignment operator duplicates the reference, not the object. The clone method provides this missing functionality.

This article describes the syntax of the C# programming language. The features described are compatible with .NET Framework and Mono.

This comparison of programming languages compares how object-oriented programming languages such as C++, Java, Smalltalk, Object Pascal, Perl, Python, and others manipulate data structures.

The programming language C# introduces several new features in version 2.0. These include:

In type theory, bounded quantification refers to universal or existential quantifiers which are restricted ("bounded") to range only over the subtypes of a particular type. Bounded quantification is an interaction of parametric polymorphism with subtyping. Bounded quantification has traditionally been studied in the functional setting of System F<:, but is available in modern object-oriented languages supporting parametric polymorphism (generics) such as Java, C# and Scala.

Ceylon (programming language)

Ceylon is an object-oriented, strongly statically typed programming language with an emphasis on immutability, created by Red Hat. Ceylon programs run on the Java virtual machine (JVM), and could be compiled to JavaScript. The language design focuses on source code readability, predictability, toolability, modularity, and metaprogrammability.

References

  1. "Chapter 4. Types, Values, and Variables". docs.oracle.com. Retrieved 2020-11-03.
  2. Gilad Bracha (June 2004), "4. Wildcards", Generics in the Java Programming Language (PDF), retrieved 6 March 2016
  3. "8.1.2 Generic Classes and Type Parameters", The Java Language Specification, Oracle, retrieved 6 March 2016
  4. Inheritance (object-oriented programming)
  5. Java syntax(Generics)