Example is pretty simple. What I want is written. The problems are in the comments.
import java.util.*;
class Main
{
private static class X<T> {
public static T get() { return new T(); }
}
public static void main(String[] args)
{
System.out.println(X<Interger>.get()); // illegal start of type
// commenting out above yields:
// error: non-static type variable T cannot be referenced from a static context
}
}
The real confounder to me is the error: non-static type variable T cannot be referenced from a static context. The error seems clear: the class & method are static but the type variable isn't. Can I make the type variable work in this static context.
Further, as the example is written, I don't understand why I get an illegal start of type error with nothing else. Is this because the error about the non-static type variable is hidden?
You can't do new T(). For one thing, there is no guarantee that T has an accessible no-args constructor. Relevant here, there is no no-arg constructor for Integer.
Static methods, as well as instance methods and constructors, can have type parameters.
public static <T> T get() {
// Can only legally return null or throw.
...
}
...
System.out.println(X.<Integer>get());
What to use instead? Possibly an abstract factory of some sort, possibly java.util.function.Supplier.
While the type X is generic, the class X is not. In Java, there are no "generic classes" (only generic types). What was most probably intended is a generic parameter on the static method:
private static class X<T> {
public static <T> T get() {
return ...;
}
}
Also, since generics are erased, one cannot instantiate T (thus the three dots in the code above).
One would call the method like such:
...
X.<SomeConcreteType>get();
I think Supplier maybe more suitable for you than static X<T>.get:
public static class aClass {}
public static <T> aMethodWantToUseX(Supplier<T> x) {
T t = x.get();
}
aMethodWantToUseX(aClass::new)
Related
Given this example code...
package example;
public class Main {
public static void main(String[] args) {
System.out.println(MyEnum.X.getValue());
}
private enum MyEnum {
X(){
#Override
String getValue() {
return toString("XYZ"); //error here
}
};
abstract String getValue();
private static String toString(String output) {
return output;
}
}
}
The following compiler error is produced:
Error:(12, 40) java: method toString in class java.lang.Enum<E> cannot be applied to given types;
required: no arguments
found: java.lang.String
reason: actual and formal argument lists differ in length
IntelliJ comes with a different issue: toString from toString("XYZ") is underlined with red, and the message "'toString(java.lang.String)' has private access in 'example.Main.MyEnum'" is shown, with the solution to "Make 'MyEnum.toString' package-private".
What's weird to me, is that any of the following fixes this issue:
Calling the method via enum reference: X.toString("XYZ").
Calling the method via class reference: MyEnum.toString("XYZ").
Calling the method via super: super.toString("XYZ"). (But this.toString("XYZ") doesn't work)
Making the method package-private or public
Naming the method "toString2"
Now, for any production code, I'd probably name the method something else (probably something more descriptive to what I'd do with it) and move on, but still I'm left wondering, why does this happen? And why are the error messages from IntelliJ and javac different?
This question is probably similar to Cannot make a static reference to the non-static field memberVariable with private variable, but I feel it doesn't fully explain the issue - why does renaming work?
First, this problem is not specific to enums. It is applicable to any inner class. I have refactored your example to remove the enum, which demonstrates the same problem with both an inner class and an anonymous inner class.
class Main {
public static void main(String[] args) {
Main main = new Main() {
{
System.out.println(toString("XYZ")); // same error
}
};
}
class Foo {
{
System.out.println(toString("XYZ")); // same error
}
}
private static String toString(String output) {
return output;
}
}
Is explained in the JLS here:
Example 6.5.7.1-1. Simple Method Names
The following program demonstrates the role of scoping when
determining which method to invoke.
class Super {
void f2(String s) {}
void f3(String s) {}
void f3(int i1, int i2) {}
}
class Test {
void f1(int i) {}
void f2(int i) {}
void f3(int i) {}
void m() {
new Super() {
{
f1(0); // OK, resolves to Test.f1(int)
f2(0); // compile-time error
f3(0); // compile-time error
}
};
}
}
For the invocation f1(0), only one method named f1 is in scope. It is the method Test.f1(int), whose declaration is in scope
throughout the body of Test including the anonymous class declaration.
§15.12.1 chooses to search in class Test since the anonymous class
declaration has no member named f1. Eventually, Test.f1(int) is
resolved.
For the invocation f2(0), two methods named f2 are in scope. First,
the declaration of the method Super.f2(String) is in scope throughout
the anonymous class declaration. Second, the declaration of the method
Test.f2(int) is in scope throughout the body of Test including the
anonymous class declaration. (Note that neither declaration shadows
the other, because at the point where each is declared, the other is
not in scope.) §15.12.1 chooses to search in class Super because it
has a member named f2. However, Super.f2(String) is not applicable to
f2(0), so a compile-time error occurs. Note that class Test is not
searched.
For the invocation f3(0), three methods named f3 are in scope. First
and second, the declarations of the methods Super.f3(String) and
Super.f3(int,int) are in scope throughout the anonymous class
declaration. Third, the declaration of the method Test.f3(int) is in
scope throughout the body of Test including the anonymous class
declaration. §15.12.1 chooses to search in class Super because it has
a member named f3. However, Super.f3(String) and Super.f3(int,int) are
not applicable to f3(0), so a compile-time error occurs. Note that
class Test is not searched.
Choosing to search a nested class's superclass hierarchy before the
lexically enclosing scope is called the "comb rule" (§15.12.1).
The following code is very confusing to me.
public class Test<T> {
public static <T> Test<T> ok(T result) {
..
I understand the generics in class name. I could understand if "ok" method would be like this
public static Test<T> ok(T result) {
or this
public static T ok(T result) {
But why there is the extra < T > before the Test < T > return type I don't understand.
The example is in fact equivalent to
public class Test<T> {
public static <U> Test<U> ok(U result) {
..
The other <T> refers to a different generic type which applies only to the method. To avoid confusion, it's better to use different names for different things.
It is a static method, so it is unaffected by the generic type parameter of an instance.
If you want the ok method to be bound by the generic parameter , you will have to make something like TestFactory<T> class as a factory that only makes tests of T type, like that:
public class TestFactory<T> {
public Test<T> ok(T result) {
// ...
}
}
So in this case a TestFactory<String> only allows String results, anything else is a compiler error.
If you prefer the static method, you should do what #jurez posted to avoid confusion: The thing is, its type parameter of this static method only depends on type of given input parameter.
I want to define a time interface like that:
public interface TimeInterface<T>
{
static T ZERO;
static T INFINITY;
// some other methods...
}
Is this possible, or how to do that to avoid errors?
Thanks in advance!
From the javadoc directly:
We cannot declare static fields whose types are type parameters
A class's static field is a class-level variable shared by all non-static objects of the class. Hence, static fields of type parameters are not allowed. Consider the following class:
public class MobileDevice<T> {
private static T os;
// ...
}
If static fields of type parameters were allowed, then the following code would be confused:
MobileDevice<Smartphone> phone = new MobileDevice<>();
MobileDevice<Pager> pager = new MobileDevice<>();
MobileDevice<TabletPC> pc = new MobileDevice<>();
Because the static field os is shared by phone, pager, and pc, what is the actual type of os? It cannot be Smartphone, Pager, and TabletPC at the same time. You cannot, therefore, create static fields of type parameters.
The only static thing that can contain type-parameters is the static method, which must define it's own type-parameters. Something like:
static <T> void staticGenericMethod(T param) { .. }
In your case, the type-parameter has a class scope, it's instance-bound and has nothing to do with the static members of the class.
So, you should either remove the static keywords for ZERO and INFINITY or introduce static methods that return ZERO and INFINITY. For example:
public interface TimeInterface<T> {
static <X> X getZero() {
//implementation
}
static <X> X getInfinity() {
}
}
Note that the X type-parameters are valid for the corresponding static method only and are not shared across the class.
The problem, however, with this approach is that there's no way to ensure that the instance type-parameter (T) is the same as the static method's type-parameter (X), which may cause serious problems when used incorrectly.
Also note that static methods are allowed in interfaces in Java8, so if you're using an order Java version, you should convert the TimeInterface to a class.
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;
}
};
}
I've read posts about why you can't have a (Edit -- generic) (which use that type parameter from the generic class) static method in a generic class, but why can you then use static generic methods in non generic classes? I don't see the why the second is allowed, but I kinda understand why the first isn't.
why you can't have a (Edit -- generic) (which use that type parameter from the generic class) static method in a generic class
The reason for this is simple: The type parameter is not associated with the class but with instances of the class.
I.e., you can't do
class Test<T> {
public static void sayHello(T t) { // T for which instance?!
System.out.println("Hello");
}
}
why can you then use static generic methods in non generic classes?
Why wouldn't you? A generic method takes the type parameter, so it doesn't matter if it's static or not, or if the class it's in is generic or not etc.
This for instance compiles fine:
class Test {
public static <T> void sayHello(T t) {
System.out.println("Hello " + t);
}
}
And you'd call the method like this:
Test.<String>sayHello("some argument");
^^^^^^^^
type parameter provided at the method-call: no instance required.