Long story short:
Why is the following not possible in Java?
public class Test<A<B>> {} // A and B both being generic parameters.
Note: I don't have any specific use case right now, rather I am just trying to understand why this is not allowed.
At first I thought because the compiler cannot assert if A can accept generics parameter because after compiling A, due to type erasure the generics won't be present anymore.
But, if that is the case, then we cannot use generics on any class at all. So I took out the byte code of a generic class and found that there is metadata to say it accepts generics.
public class com.Test<T> {
public com.Test();
Code:
0: aload_0
1: invokespecial #12 // Method java/lang/Object."<init>":()V
4: return
}
And I did a quick search and SO confirmed compiled code will have generics related metadata too
So why does the compiler not allow multilevel generics?
Will there be any problem in allowing it? Is it a limitation? or something else?
Let's assume that this class was actually compiling:
public class Test<A<B>> { .. }
This implies that a proper instantiation of the class would be:
new Test<Class<Integer>>()
//or
new Test<List<String>>()
and the following wouldn't be correct (since the provided type-parameter is not generic):
new Test<String>();
//or
new Test<Object>();
However, a type-parameter should not be restricted for being generic or not - it should just hold some meta information about the type (and as it turns out, this is the type after type-erasure has taken place) with which it would be replaced.
Type-erasure itself can be another possible reason for not allowing such constructions. Let's again consider the above Test class was correctly defined and you had this:
new Test<Class<Integer>>();
When type-erasure happens, <A<B>> should be replaced with Class<Integer>, however, due to the erasure, we'd have only a Class (even though internally it would contain info about the Integer type) - expecting a Class<Integer>, but providing a Class should not be correct.
Thanks for the interesting question!
Scala calls it higher kinded types. So it's definitely a feasible abstraction. Adding it to Java has — to my knowledge — never been seriously considered.
Sadly, I can't find any good introductory text to Scala's higher kinded types. The best I can find is the original paper Generics of a Higher Kind; here's its abstract:
With Java 5 and C# 2.0, first-order parametric polymorphism was introduced in mainstream object-oriented programming languages under the name of generics. Although the first-order variant of generics is very useful, it also imposes some restrictions: it is possible to abstract over a type, but the resulting type constructor cannot be abstracted over. This can lead to code duplication. We removed this restriction in Scala, by allowing type constructors as type parameters and abstract type members. This paper presents the design and implementation of the resulting type constructor polymorphism. Furthermore, we study how this feature interacts with existing object-oriented constructs, and show how it makes the language more expressive.
Related
I'm trying to understand the benefit of a programming language being statically typed, and through that, I'm wondering why we need to include type in declaration? Does it serve any purpose rather than to make type explicit? If this is the case, I don't see the point. I understand that static typing allows for type checking at compile-time, but if we leave out the explicit type declaration, can't Java still infer type during compile-time?
For example, let's say we have in Java:
myClass test = new myClass();
Isn't the type declaration unnecessary here? If I'm not mistaken, this is static binding, and Java should know test is of type myClass without explicit declaration of type even at compile-time.
Response to possible duplicate: this is not a question regarding static vs. dynamic type, but rather about type inference in statically typed languages, as explained in the accepted answer.
There are statically typed languages that allow you to omit the type declaration. This is called type inference. The downsides are that it's tougher to design (for the language designers), tougher to implement (for the compiler writers), and can be tougher to understand when something goes wrong (for programmers). The problem with the last one of those is that if many (or all) of your types are inferred, the compiler can't really tell you much more than "the types aren't all consistent" — often via a cryptic message.
In a trivial case like the one you cite, yes, it's easy. But as you get farther from the trivial case, the system quickly grows in complexity.
Java does actually do a bit of type inference, in very limited forms. For instance, in this snippet:
List<String> emptyStrings = Collections.emptyList();
... the compiler has inferred that the method call emptyList returns a List<String>, and not just a List<T> where the type T is unspecified. The non-inferred version of that line (which is also valid Java) is:
List<String> emptyStrings = Collections.<String> emptyList();
It is necessary. You can have inheritance, where types are necessary.
For example:
Building build1 = new House();
Building build2 = new SkyScraper();
It is the same in polymorphism.
You can then collect all Buildings to array for example. If there will be one House and one SkyScraper you can't do this.
As part of the compiler for the programming language I am working on, I came across generic signatures in the bytecode, which I am trying to parse and convert to an AST. The parsing algorithm mostly works, but there seems to be a special case in which the format of these signatures behaves a bit strangely. Here are a few of these cases:
java.util.Arrays#parallelSort: <T::Ljava/lang/Comparable<-TT;>;>([TT;)V
java.util.Arrays#parallelSort: <T::Ljava/lang/Comparable<-TT;>;>([TT;II)V
java.lang.Class#getAnnotation: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)TA;
java.lang.Class#getAnnotationsByType: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)[TA;
java.lang.Class#getDeclaredAnnotation: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)TA;
java.lang.Class#getDeclaredAnnotationsByType: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)[TA;
java.util.Arrays#parallelSort: <T::Ljava/lang/Comparable<-TT;>;>([TT;)V
java.util.Arrays#parallelSort: <T::Ljava/lang/Comparable<-TT;>;>([TT;II)V
java.util.Collections#sort: <T::Ljava/lang/Comparable<-TT;>;>(Ljava/util/List<TT;>;)V
Out of all the methods in these classes, these are the only ones that have :: in their signature. My question is what this token does and why it exists.
Edit
I know about the :: operator in the Java Language, but this is something on the Bytecode level.
There is a defined syntax that changed as of JSR 14 to specify the bounds of a generic type.
variable_name:class_type_bound:interface_type_bounds
So for your example of:
<T::Ljava/lang/Comparable<-TT;>;>
Which would reflect:
<T extends Comparable<T>>
The variable name is T, there is no class type bound so it was omitted, and there was an interface bound of type Comparable<T>.
All your example follow this, but there any many different forms:
<T:Ljava/lang/Object;>(Ljava/util/Collection<TT;>;)TT;
<T::Ljava/lang/Comparable;>(Ljava/util/Collection<TT;>;)TT;
<T:Ljava/lang/Object;:Ljava/lang/Comparable;(Ljava/util/Collection<TT;>;)TT;
Source
I was surprised today when this code compiled:
class GenericClass<T> {
public void emptyMethod(T instance) {
// ..
}
public void print(T instance) {
System.out.println(instance);
}
}
public class Main {
public static void main(String[] args) {
GenericClass first = new GenericClass();
System.out.println("Wow");
first.emptyMethod(10);
first.print(16);
}
}
The compiler emits a warning (Type safety: The method emptyMethod(Object) belongs to the raw type GenericList. References to generic type GenericList should be parameterized), but anyway it does not cause a compiler error and it runs 'fine' (at least the provided print method). As I'm understanding, the compiler is using object as the type argument, but I find it counter-intuitive. Why would the compiler do such thing? Why it doesn't require me to specify the type parameter?
You're using a raw class, basically.
Think back to when generics were first introduced in Java: there was a load of code which already used List, ArrayList etc. In order to avoid breaking all of that code, but still reusing the existing classes, raw types were introduced - it's basically using a generic type as if it weren't one.
As you can see, you get a warning - so it's worth avoiding - but that's the primary reason for it being allowed at all.
See section 4.8 of the JLS for more information, which includes:
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.
You have to know how generics are implemented in Java. They are far from perfect.
You have to remember that during run time everything is an Object. There are no types during run time.
Generics were added for added security in places, where you need it, but if you don't want to use it, you can ignore warnings and use unparametrized instances.
However, if you'd like java compiler to help you with type safety, then you parametrize generic class instances. Once you create a GenericClass for example, compiler will not allow you to use it with an integer parameter (first.emptyMethod(10) will not compile). You can still make it work with integer parameter if you do explicit type casting though.
So consider it a good practice for added security, which only works if you follow the rules.
I am frequently pushing the limits of Java's type system through my use of Guice, TypeLiteral, generics, and wildcards. I often run into situations where I need to perform unchecked casts, which pretty much ruins type safety--in other words, "Generics Hell."
Here's a simplified example of some of my problematic Java code.
class SquareDrawer implements ShapeDrawer<Row<Square>> {}
class Client {
Key<SquareDrawer> SQUARE_DRAWER_KEY =
Key.get(SquareDrawer.class, randomAnnotation());
void bindShapeDrawer(
Key<? extends ShapeDrawer<Row<? extends Shape>>> shapeDrawer) {}
Client() {
// Note Unchecked cast required below
bindShapeDrawer(
(Key<? extends ShapeDrawer<Row<? extends Shape>>>) SQUARE_DRAWER_KEY);
}
}
I've been learning Scala and have been under the impression (or illusion) that it has better support for generics than Java. Could the above code be written in Scala to avoid the unchecked casts?
Is there still a need for Guice's TypeLiteral in Scala?
There are a couple of things Scala offers.
Higher kinded types (I hope I use the term correctly) allow you to define things like 'any type having another type as a type parameter' afaik there is no way to express that in java
Co and Contravariant type parameters. In java you can make parameters one or the other by using wildcards in every place where they are used. In Scala you just declare them as such.
Type witnesses (Again: Is that the correct term?) are implicit functions that demonstrate some property of type arguments, thereby defining constraints on the type. If an implicit conversion matching the declaration of the witness exists, the call will compile the condition holds.
Path dependent types. You can have types that are elements of instances, so each instance has its own type. Again you can't do this in java afaik.
Scala has a form of reified types called Manifests. They let you do things that would be quite clunky in Java due to type erasure. Read all about them here: http://www.scala-blogs.org/2008/10/manifests-reified-types.html
The following (hopefully) equivalent Scala code compiles without errors. Maybe I need to state that it contains no dynamic casts. Note that I had to make Key covariant in its type argument, because SquareDrawer is only a subtype of ShapeDrawer[Row[Square]].
trait ShapeDrawer[A]
trait Row[A]
trait Shape
trait Square extends Shape
trait Key[+A]
//your code starts here
trait SquareDrawer extends ShapeDrawer[Row[Square]]
class Client{
val SDK = new Key[SquareDrawer]{}
bindShapeDrawer(SDK)
def bindShapeDrawer[SD[A] <: ShapeDrawer[A],S <: Shape](shapeDrawer: Key[SD[Row[S]]]) {}
}
For example, I could write either of these:
class example <T>
{
...
public void insert (T data)
{
...
}
}
or
class example
{
...
public void insert (Object o)
{
...
}
}
Is there a signficant difference between the 2 in terms of performance? With generics I could restrict the type of the parameter and with the second approach I guess it wouldn't be necessary to define the type of the object as it is created.
Also, with the second approach I could basically insert anything into the class, right? Whereas with generics every element in the class would be of the same type.
Anything else I'm missing?
The only reason to write the latter is if you must target an earlier JVM. Generics are implemented by type-erasure, so they have no runtime impact - only added compile time checking which will improve your code.
Of course if you need a collection which holds any old object, or a mix of several which don't have a common superclass, you need the plain Object variation (but then your class can still be generic and instantiated with new ...<Object>).
I think you pretty much nailed it. There is no performance difference. Generics are rationalized away (Type Erasure) when the code is compiled, and don't exist anymore at runtime. They just add casts when needed and do type-checking as you stated. Neal Gafter wrote a nice overview of how they work, of the current problems with Generics and how they could be solved in the next version of Java: http://gafter.blogspot.com/2006/11/reified-generics-for-java.html
There shouldn't be a performance difference.
However, Java does not offer parameter variance, so there are situations where you will be overriding pre-generics functions such as equals, compareTo, etc. where you will have to use Objects.
Some of the encounters where I had to use 'Object' instead of Generics were those of compulsion than of a choice. When working with pre-generic code or libraries built around pre-generic api, one has little choice. Dynamic proxies for example, Proxy.newProxy() returns Object type. Passing generic context (where a context can be anything) is another instance. Some of my friends argue that are as good as no-generics. As far as performance is concerned, there shouldn't be any overhead, considering type erasure.
Regarding performance, i agree with the people above.
Regarding this point of yours
"Also, with the second approach I could basically insert anything into the class, right? Whereas with generics every element in the class would be of the same type."
One more advantage of generics is there is a type check for assignment of the example instance itself.
Say for example you had an Example e1 of and another Example e2 of , type safety would be maintained and you would never be able to do e1=e2;
while with the object example, that would be possible.