Java theory and practice: Going wild with generics, Part 1
We'll occasionally send you account related emails. .. We're simply unable to compute what its own bound means, and hence we cannot instantiateToBounds sometimes fails to eliminate type parameters # I think there's a proof to be made about the typing relationship of of infinite types which is. When a type parameter is specified as covariant (with +), it can change into a tuple class with the same number of parameters gets instantiated with the values. . We just devoted several sections in this chapter to type-related features such They only work on types, also, so objects cannot be used to create type aliases . This content is part of the series:Java theory and practice . On the other hand, generics are not covariant; List is not a subtype of The only thing it cannot do is call the put() method, and this is because it there is no relationship between any of the unknown type parameters. . Related topics.
Get unlimited access to videos, live online training, learning paths, books, tutorials, and more. Advanced Typing By this point in the book you should have a pretty good understanding of the Scala language. If you have read the chapters and pursued the exercises, then you are already pretty good at defining classes, writing functions, and working with collections.
You know everything you need to in order to go out and start building your own Scala applications. In it we will cover many of the type features that make the language possible.
One interesting feature is how the apparently high-level tuples and function literals are built with regular classes.
Their fancy syntax belies their humble foundation, which you can validate by creating them as class instances: The type parameters we have used for classes, traits, and functions are actually quite flexible.
Base Base Type parameters can also morph into compatible types, even when bound in a new instance. The List collection is covariant, so a list of a subclass can be converted to a list of a base class: You will also be better able to understand the official Scala library documentation, as the library makes heavy use of advanced type features.
Finally, they will help you to see and understand the machinery that installs many Scala features in place. Tuple and Function Value Classes If you have already read the previous chapters in this book there will be no need to introduce tuples and function values to you. They were well covered in Tuples and Function Types and Valuesrespectively.
The syntax shortcuts to create these instances are short and expressive, but the actual implementation is plain old classes that you could have written yourself. The good news is it means these high-level constructs are backed by safe, type-parameterized classes.
When you create a tuple with the parentheses syntax e. In other words, the expressive syntax of tuples is simply a shortcut to a case class you could have written yourself. These traits offer operations such as productArity, returning the arity of the tuple, and productElement, a nontype-safe way to access the nth element of a tuple. Function value classes are similar but provide a logic-centric implementation. Function values are implemented as instances of the FunctionX[Y] trait, numbered from 0 to 22 based on the arity of the function.
In other words, when you write a function literal, the Scala compiler converts it to the body of the apply method in a new class extending FunctionX. And the Function1 class, along with all of the other FunctionX classes, overrides toString with its name in lowercase surrounded by angle brackets. The Function1 trait contains two special methods not available in Function0 or any of the other FunctionX traits.
You can use them to combine two or more Function1 instances into a new Function1 instance that will execute all of the functions in order when invoked. The only restriction is that the return type of the first function must match the input type of the second function, and so on. The method andThen creates a new function value from two function values, executing the instance on the left followed by the instance on the right.
The method compose works the same way but in opposite order. Implicit Parameters In Partially Applied Functions and Currying we studied partially applied functions, where a function could be invoked without its full set of parameters.
The result was a function value that could be invoked with the remaining set of unspecified parameters, invoking the original function. What if you could invoke a function without specifying all of the parameters, but the function would actually be executed? The missing, unspecified parameters would have to come from somewhere to ensure the function would operate correctly. One approach would be to define default parameters for your function, but this would require having the function know what the correct values for the missing parameters should be.
Another approach is to use implicit parameters, where the caller provides the default value in its own namespace. Functions can define an implicit parameter, often as a separate parameter group from the other nonimplicit parameters. Invokers can then denote a local value as implicit so it can be used to fill in as the implicit parameter. When the function is invoked without specifying a value for the implicit parameter, the local implicit value is then picked up and added to the function invocation.
Use the implicit keyword to mark a value, variable, or function parameter as being implicit. An implicit value or variable, if available in the current namespace, may be used to fill in for an implicit parameter in a function invocation. Fortunately, adding the explicit parameter works fine.
They mostly provide functionality that callers can choose to override but otherwise may ignore, such as collection builders or default collection ordering. If you use implicit parameters, keep in mind that excessive use can make your code hard to read and understand. Finding out that their function invocation included implicit parameters without their knowledge may be an unwelcome surprise. Implicit Classes Another implicit feature in Scala, similar only in nature to implicit parameters, is implicit conversions with classes.
An implicit class is a type of class that provides an automatic conversion from another class. By providing an automatic conversion from instances of type A to type B, an instance of type A can appear to have fields and methods as if it were an instance of type B.
What About Implicit Defs? Implicit methods have been supplanted by implicit classes, which provide a safer and more limited scope for converting existing instances. If you want to use implicit defs in your own code, see the scala. The Scala compiler uses implicit conversions when it finds an unknown field or method being accessed on an instance. It checks the current namespace for any implicit conversion that 1 takes the instance as an argument and 2 implements that missing field or method.
If it finds a match it will add an automatic conversion to the implicit class, supporting the field or method access on the implicit type. Before using it, the implicit class must be added to the namespace … … and then the fishes method can be invoked on any integer. Implicit classes make this kind of field and method grafting possible, but there are some restrictions about how you can define and use them: An implicit class must be defined within another object, class, or trait.
Fortunately, implicit classes defined within objects can be easily imported to the current namespace. They must take a single nonimplicit class argument. In the preceding example, the Int parameter was sufficient to convert an Int to a Fishies class in order to access the fishes method. Thus, a case class could not be used as an implicit class because its automatically generated companion object would break this rule.
The preceding example follows all of these rules. Although you can implement your implicit classes in objects, classes, or traits, I find it works better to implement them in objects.
Advanced Typing - Learning Scala [Book]
To be precise, you will never automatically pick up an implicit conversion other than the ones in the scala. The members of this object, a part of the Scala library, are automatically added to the namespace. But because the implicit conversion was added to the namespace… implicitly… the expression is no greater than two values separated by an arrow.
Implicit classes are a great way to add useful methods to existing classes. Used carefully, they can help to make your code more expressive. Take care to avoid hurting readability, however. A class is an entity that may include data and methods, and has a single, specific definition. A type is a class specification, which may match a single class that conforms to its requirements or a range of classes. The Option class, for example, is a type, but so is Option[Int].
Types are class specifications but work equally well with traits. Objects, however, are not considered to be types. They are singletons, and while they may extend types they are not types themselves. They can help you to write stricter, safer, stabler, and better-documented code, which is the entire point of having a strong type system. Type Aliases A type alias creates a new named type for a specific, existing type or class. This new type alias is treated by the compiler as if it were defined in a regular class.
You can create a new instance from a type alias, use it in place of classes for type parameters, and specify it in value, variable, and function return types. If the class being aliased has type parameters, you can either add the type parameters to your type alias or fix them with specific types. Like implicit conversions, however, type aliases can only be defined inside objects, classes, or traits.
They only work on types, also, so objects cannot be used to create type aliases. You can use the type keyword to define a new type alias. Also, the type UserInfo is an alias for a tuple with an integer in the first position and a string in the second. Because a Tuple2 is an instantiable case class, we were able to instantiate it directly from the type alias UserInfo. Type aliases are a useful way to refer to existing types with a local, specific name. However, as with other advanced type features, type aliases should not replace careful object-oriented design.
A real class named UserInfo will be more stable and intuitive in the long term than using type aliases. Abstract Types Whereas type aliases resolve to a single class, abstract types are specifications that may resolve to zero, one, or many classes.
They work in a similar way to type aliases, but being specifications they are abstract and cannot be used to create instances.
Abstract types are popularly used for type parameters to specify a range of acceptable types that may be passed. They can also be used to create type declarations in abstract classes, which declare types that concrete nonabstract subclasses must implement.
As an example of the latter, a trait may contain a type alias with an unspecified type. That type declaration can be reused in method signatures, and must be filled in by a subclass. In a concrete subclass the type is redefined with a type alias to a specific class.
Another way of writing this trait and class would be to use type parameters. If you want a parameterizable type, then type parameters work great. Otherwise, abstract types may be more suitable.
The UserFactory example class works just as well with a parameterizable type versus defining its own type alias. In this example there were no restrictions on the type allowed for subclasses of the Factory trait.
However, it is often more useful to be able to specify bounds for the type, an upper or lower bound that ensures that any type implementation meets a certain standard.
Bounded Types A bounded type is restricted to being either a specific class or else its subtype or base type. DemoClass is simple java class, which have one property t can be more than one also ; and type of property is Object. Since we have declared property type to Object, there is no way to enforce this restriction.
A programmer can set any object; and can expect any return value type from get method since all java types are subtypes of Object class. To enforce this type restriction, we can use generics as below: A sample usage of DemoClass will look like this: Generic Type Method or Constructor Generic methods are much similar to generic classes.
They are different only in one aspect that scope of type information is inside method or constructor only. Generic methods are methods that introduce their own type parameters. Below is code sample of a generic method which can be used to find all occurrences of a type parameters in a list of variables of that type only.
But if you will try to find an Number into list of String, it will give compile time error. Same as above can be example of generic constructor. So you can have an instance of dimension with all attributes of a single type only. In java, pushing any incompatible type in an array on runtime will throw ArrayStoreException.
It means array preserve their type information in runtime, and generics use type erasure or remove any type information in runtime. Due to above conflict, instantiating a generic array in java is not permitted. As we know that an array is a collection of similar type of elements and pushing any incompatible type will throw ArrayStoreException in runtime; which is not the case with Collection classes.
It can happen anytime. Another reason why arrays does not support generics is that arrays are co-variant, which means that an array of supertype references is a supertype of an array of subtype references. That is, Object is a supertype of String and a string array can be accessed through a reference variable of type Object. A wildcard parameterized type is an instantiation of a generic type where at least one type argument is a wildcard.
The wildcard can be used in a variety of situations: The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype.
Having wild cards at difference places have different meanings as well. Collection denotes all instantiations of the Collection interface regardless of the type argument.
Going wild with generics, Part 1
List denotes all list types where the element type is a subtype of Number. A wildcard parameterized type is not a concrete type that could appear in a new expression. It just hints the rule enforced by java generics that which types are valid in any particular scenario where wild cards have been used. For example, below are valid declarations involving wild cards: Unbounded wildcard parameterized type A generic type where all type arguments are the unbounded wildcard "?
Here Integer, Double are subtypes of Number class. In below given example, I have created three classes i. There relationship is shown in code below. Now, we have to create a method which somehow get a GrandChildClass information e. So far we have learned about a number of things which you can do with generics in java to avoid many ClassCastException instances in your application. We also saw the usage of wildcards as well.
Any attempt to do so will generate compile time error: Cannot make a static reference to the non-static type T. Cannot instantiate the type T.