I'm writing generic class
public class SomeClass<T> {
public static <T extends Comparable<? super T>> T min(Collection<? extends T> c) {
T min = c.iterator().next();
for (T element : c)
if (element.compareTo(min) < 0)
min = element;
return min;
}
}
public class Main {
public static void main(String[] args) {
SomeClass<Integer>.min(Arrays.asList(1, 2, 3)); // compile-time error
SomeClass.min(Arrays.asList(1, 2, 3)); // ok
}
}
In generic class SomeClass and generic method SomeMethod type-parameter T is the same or defference?
Why we have compile time-error on the string SomeClass<Integer>.min(Arrays.asList(1,2,3));?
The class declaration
public class SomeClass<T>
defines a generic class, where <T> specifies the type parameter (also called type variable). This introduces the type variable, T, that can be used anywhere inside the class.
And the method declaration:
public static <T extends Comparable<? super T>> T min(Collection<? extends T> c) {
...
}
defines a generic method. Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared.
Now if you want to call the generic method min, you need to call:
SomeClass.<Integer>min(Arrays.asList(1,2,3));
It's different, and you should never write code like this, specifically because of the possibility for confusion. Always use different type variables on classes and methods inside those classes.
T is for Type and you are accessing static method which has nothing to do with T.
Either use like this
SomeClass<Integer> a = new SomeClass<Integer>();
a.min(Arrays.asList(1, 2, 3));
OR
SomeClass.min(Arrays.asList(1, 2, 3));
The two Ts are different: the fist one is a parameter for the class (and is unused) and the second one is specific to the method. Since the method is static, the class parameter doesn’t affect it.
When you write SomeClass<Integer>.min(Arrays.asList(1, 2, 3));, you get an error because it doesn’t make sense to add a parameter to SomeClass since no object of that class is instanciated. SomeClass is only used to tell the compiler that you want to call a static method from that class. You can add a parameter to the method with SomeClass.<Integer>min(Arrays.asList(1, 2, 3));, but you don't have to since the compiler can infer the type here.
So what the compile message told you was that it's a syntax error. Why it's invalid syntax is easy to understand. You are trying to call a method on a type. That means you are calling a static method. Static methods are not within the scope of generic type parameters. So it is not allowed to put a generic type argument on the left side.
If you want the technical reason, it's because your method call expression is not one of the forms that's allowed by the syntax. The closest form to yours is TypeName . NonWildTypeArguments Identifier ( ArgumentListopt ). But TypeName (which is defined here) must be an identifier or package-qualified identifier. It does not allow brackets.
Related
I am struggling to understand how variance works in Java.
In the following example, I define a function test which takes a Consumer. The function is defined without contravariance, so I would expect that Consumer<Object> is not a subtype of Consumer<Pair<Animal, Animal>>. Yet, the code compiles, and test accepts the lambda Variance:::superAction.
What am I missing?
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.util.function.Consumer;
public class Variance {
public static void main(String[] args) {
test(Variance::exactMatchAction);
test(Variance::superAction);
}
private static void exactMatchAction(Pair<Animal, Animal> pair) {
System.out.println(pair.getLeft().getClass().getName());
}
private static void superAction(Object obj) {
System.out.println(obj.getClass().getName());
}
private static void test(Consumer<Pair<Animal, Animal>> action) {
action.accept(ImmutablePair.of(new Animal(), new Animal()));
action.accept(ImmutablePair.of(new Dog(), new Dog()));
}
static class Animal { }
static class Dog extends Animal { }
}
Edit: Per #Thielo's comment, the reference superAction is desugared to a Consumer<Pair<Animal, Animal>> NOT a Consumer<Object>.
The correct type to give the test method is something like:
void test(Consumer<? super Pair<? extends Animal, ? extends Animal>>)
This type will allow us to pass a Consumer<Object> to test, and also allow us to call the consumer with arguments like Pair<Dog, Dog> instead of just Pair<Animal, Animal>.
As a follow-up question, with this updated type for test, it will not accept a method reference like void exactMatchAction<Pair<Animal, Animal>> anymore, only void exactMatchAction<Pair<? extends Animal, ? extends Animal>>. Why is this?
Method reference expressions (such as your Variance::superAction) are poly expressions (JLS8, 15.13). The type of a poly expression may be influenced by the expression's target type (JLS8, 15.3), which is the type expected in that context (JLS8, 5), i.e., Consumer<Pair<Animal, Animal>>, in your case.
The details are spelled out in JLS8, 15.13.2. The basic idea is that there is special handling for functional interface types such as Consumer. Specifically, the method type only need be congruent to the function type (which is Pair<Animal, Animal> -> void-- note that Consumer has vanished from the type consideration here), which is satisfied by "identif[ying] a single compile-time declaration corresponding to the reference" (and having void as return type). Here, the notion of "identifying" a declaration goes back to 15.12.2 and basically describes the method overload resolution process. In other words, the language now takes the function parameters expected for Consumer<Pair<Animal, Animal>>.accept() (i.e., Pair<Animal, Animal>) and checks whether the method reference could be called with that (this resolves overloading in case there are multiple static methods with the same name).
I'm trying to understand the name clash error I get with the following code:
import java.util.*;
import javax.swing.*;
class Foo<R extends Number> {
public void doSomething(Number n, Map<String, JComponent> comps) {
}
}
class Bar extends Foo {
public void doSomething(Number n, Map<String, JComponent> comps) {
}
}
Error message:
error: name clash: doSomething(Number,Map<String,JComponent>) in Bar
and doSomething(Number,Map<String,JComponent>) in Foo have the same
erasure, yet neither overrides the other
I know I can fix it by either remove the generic type from Foo, or by changing the Bar declaration to class Bar extends Foo<Integer>; what I want to know is why this error occurs in the specific case, but goes away if I remove the comps parameter from each method. I've done some reading about type erasure, but it still appears to me that both methods should have the same erasure with or without generics, and therefore be a valid override in either case. (Note that I haven't even used the generic parameter anywhere yet, which is why I'm so surprised.)
I know that I've added generic types to parent classes before but only got warnings about the subclasses, not errors. Can anyone explain this scenario?
Luiggi is right in the comments. This is a consequence of raw types.
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
super are treated as method calls on a raw type.
This applies when invoking a supertype method, but also when overriding one.
Take for example, the following
class Bar extends Foo {
public Bar() {
doSomething(1, new HashMap<Number, String>());
}
}
You'll notice that it compiles, even though HashMap<Number, String> is not a type that is assignable to Map<String, JComponent>.
The type of a constructor (§8.8), instance method (§8.4, §9.4), or
non-static field (§8.3) of a raw type C that is not inherited from its
superclasses or superinterfaces is the raw type that corresponds to
the erasure of its type in the generic declaration corresponding to C.
(Note that C in our case is Bar.)
And the same thing happens when trying to override a method. When trying to override the Foo#doSomething(..) method, your Bar class is actually seeing it declared as
public void doSomething(Number n, Map comps) {
}
In other words, every usage of type parameters is erased. So attempting to declare the method
public void doSomething(Number n, Map<String, JComponent> comps) {
}
in the subtype Bar is actually an attempt at overloading, not overriding. And this fails because of type erasure. The proper override, which you can verify with #Override, is
public void doSomething(Number n, Map comps) {
}
Further reading:
What is a raw type and why shouldn't we use it?
Trying to create a static field with a generic type doesn't compile:
class MyClass {
public static Function<Z, Z> blargh = new Function<Z, Z>() {
public Z apply(Z a) {
return a;
}
};
}
Eclipse says:
Multiple markers at this line
- Z cannot be resolved to a type
- Z cannot be resolved to a type
- Z cannot be resolved to a type
- Z cannot be resolved to a type
- The type new Function<Z,Z>(){} must implement the inherited
abstract method Function<Z,Z>.apply(Z)
but replacing all the Zs with a concrete type works just fine:
static Function<Integer, Integer> blargh = new Function<Integer, Integer>() {
public Integer apply(Integer a) {
return a;
}
};
What's going on here?
Context:
I was originally trying to figure out why this code uses a method instead of a field:
public static <T extends Throwable> F<T, String> eMessage() {
return new F<T, String>() {
public String f(final Throwable t) {
return t.getMessage();
}
};
}
Maybe it's to overcome this restriction?
the Function type is from Google's guava library.
Edit: Now I see the problem better.
I think that firstly you would have to declare the type as a class parameter:
class MyClass<Z> {
to get visibility, but now the reason you can't use it like that is because the static member should be shared among all the instances of the class. But since you could create instances with different type parameters, the static member depending on a particular type would not make sense.
You can only use class-level generics on member fields. For example:
public class MyClass<Z> {
private Function<Z, Z> function;
// ...
}
is correct. Declaring this static instead will break. Why?
Think about ArrayList. Its class declaration is something like:
public class ArrayList<E> extends AbstractList<E> implements List<E>, ... {
// ...
}
E has no context in a static sense, because static variables belong to all instances of ArrayList, but E can be different for each ArrayList instance:
// Here's one ArrayList with E as String
List<String> strs = new ArrayList<String>();
// And another with E as Boolean
List<Boolean> bools = new ArrayList<Boolean>();
So because E can change from instance to instance, it doesn't make sense to have an E variable at the static level.
Now you can declare static methods with generics, but in a totally different way. For example, Collections.sort could have a declaration like this:
public static <T> void sort(List<? extends T> list, Comparator<T> comparator)
Notice that T is declared as part of the method before the return type. This is defining the context of T within the method, and T can differ from call to call.
Post-edit remark: in your case, you don't have Z declared anywhere, so you won't be able to use it anyway. See my declaration above for MyClass. Notice how I used <Z> directly on the class? That means that Z will be some arbitrary type.
In the case of what you were trying to figure out, you should look at Function as a generic way of representing a transformation. Let's dissect your posted method:
public static <T extends Throwable> F<T, String> eMessage() {
return new F<T, String>() {
public String f(final Throwable t) {
return t.getMessage();
}
};
}
First, note that this is a method, not a static field like your OP, so it's legal to have generics here. Also, it's static, so any generics need to be declared before the return type. Here, they declare <T extends Throwable>, so T must be some kind of error or exception that extends Throwable. The return type is F<T, String>, which is a function that takes a T (a Throwable) and returns a String. The actual object declares an f method which does just that by calling Throwable.getMessage. Since the project is functionaljava, everything is based on the F class, so generics are everywhere.
Just remember:
Generics declared at the class level can only be used by non-static members and methods.
Generics declared at the method level are allowable, but don't refer to the class-level types, referring instead to types declared before the return type.
Generics declared at the static field level simply aren't allowed because they'll never have context for their concrete type.
I think the simplest answer might be that: although the JDK compiler is flexible in how it interprets generics, it is impossible to modify or specify the "Z" class given the semantics of your code.
In all use of generics, you must define a syntax which specifies the identity of the generic class that is being operated upon. For example (As in the examples above).
1) Use a generic, parameterized utility function. In this case, its obvious to the compiler because the specified class is sent as input the function.
2) Define the class itself as being generic, and non static. This would then require that the user of the class declare it with the proper specified class parameter.
Specifically, for Function classes, you are clearly defining a constrained class : one which takes "Z" as input, and returns "Z" as output. If you want to generify this, you might create a FunctionFactory class, which takes in, for example, a single instance of Z, and returns a type-specified function of type :
public static <Z> Function<Z,Z> functionFactory(final Z default){
return new Function<Z,Z>(){
#Override
public Z apply(Z input) {
// TODO Auto-generated method stub
if(input==null)
return default;
else
return input;
}
};
}
This code seems to work fine
class Rule<T>
{
public <T>Rule(T t)
{
}
public <T> void Foo(T t)
{
}
}
Does the method type parameter shadow the class type parameter?
Also when you create an object does it use the type parameter of the class?
example
Rule<String> r = new Rule<String>();
Does this normally apply to the type parameter of the class, in the situation where they do not conflict? I mean when only the class has a type parameter, not the constructor, or does this look for a type parameter in the constructor? If they do conflict how does this change?
SEE DISCUSSION BELOW
if I have a function call
x = <Type Parameter>method(); // this is a syntax error even inside the function or class ; I must place a this before it, why is this, and does everything still hold true. Why don't I need to prefix anything for the constructor call. Shouldn't Oracle fix this.
All of your Ts are different, but you can only see it if you call your methods with the complete syntax:
For example, this code is valid:
new <Float>Rule<Integer>().<Character>Foo();
Just to make this easier to explain, let's assume your code is this:
class Rule<A>
{
public <B>Rule()
{
}
public <C> void Foo()
{
}
}
Then you can explicitly declare generic types like:
new <B>Rule<A>().<C>Foo();
If the types have the same name, the inner-most one will be chosen (the T on the method, not the class):
With this code, taking parameters:
class Rule<T>
{
public <T>Rule(T t)
{
}
public <T> void Foo(T t)
{
}
}
Then this is valid:
new <Float>Rule<Integer>(3.2f);
Note that T in the constructor is Float, not Integer.
Another example:
class Example<T> {
public <T> void foo1() {
// T here is the <T> declared on foo1
}
public void foo2() {
// T here is the <T> declared on the class Example
}
}
I found another question that deals with calling methods with explicit generic types without something before them. It seems like static imports and same-class method calls are the same. It seems like Java doesn't let you start a line with <Type> for some reason.
Does the method type parameter shadow the class type parameter?
The <T> declaration on constructor is not referred to class type. So yes, it shadow the class type parameter.
In this case it is used as a generic type param that you can use with the constructor, for example as argument. Try this constructor:
public <P> Rule(P arg1, P arg2) {
}
As you can see I define a type <P> and then I use it to be sure that the arguments will be of type P. In your case you are declaring a type that will be valid for the constructor without using it.
Look at this page.
Also when you create an object does it use the type parameter of the class?
Every generic type definition has is scope as a variable. So out of the constructor returns valid the class type.
Just wonder why type parameter are not allowed after the class name on the constructor. I mean what's the reason behind this. Is it becos' the type parameter already defined on the class header and so doesn't make sense to have it on the constructor?
Class A <E> {
public E e;
A <E> {
}
}
Just curious
You can define type parameters for a constructor, using the same syntax used for methods.
However, it's important to realize this is a new type parameter, visible only during execution of the constructor; if it happens to have the same name as a type parameter on the class, it will hide that parameter in the larger scope.
class Foo<T>
{
<T> Foo(T bar) /* This "T" hides the "T" at the class level. */
{
...
If you define generics in class level they must be declared during declaration of class.
class A<T>{}
Do you want to declare T when declaring constructor, i.e. something like this:
class A {
public A<T>() {
}
}
But in this case you cannot use T before constructor when you wish to declare fileds:
class A {
private T t; // this will throw compilation error: T is undefined.
public A<T>() {
}
}
I think that this is the reason that Sun defined existing syntax for generics.
Although you can use generic type as parameter of constructor:
class A<T> {
public A(T t) {
}
}
Well, at least the following seems to compile in Eclipse:
public class A{
private boolean same;
public <T> A(T t1, T t2, Comparator<? super T> comparator){
this.same = (comparator.compare(t1, t2) == 0);
}
...
}
As the name says, it is a type parameter, and so its scope is wider than just a constructor or a method.