2016-03-28 54 views
4

在试图解决问题时,我最终编写了一个类型参数的方法,该参数在参数和返回类型中均未提及,即<T> void authenticate(Credentials credentials)方法。 T的唯一依赖关系在方法体中(详细内容见前)。所以我的问题是:那个泛型类型怎么解决?我找不到任何其他具有这种泛型类型参数的示例代码。Java如何解析不在方法声明中的泛型类型参数?

所以该方法的身体是一样的东西

public <T extends AuthResponse> void authenticate(Credentials credentials) { 
    Call<T> call = getAuthCall(credentials); 
    Response<T> response = call.execute(); 

    // doSomething is an AuthResponse method 
    response.body().doSomething(); 
} 

它调用的方法只有一个通用的返回类型,它是这样的:

private Call<T extends AuthResponse> getAuthCall(Credentials credentials) { 
    if (credentials instanceof CredentialsA) 
     return (Call<T>) service.authenticate((CredentialsA) credentials); 
    else if (credentials instanceof CredentialsB) 
     return (Call<T>) service.authenticate((CredentialsB) credentials); 
    else 
     throw new IllegalArgumentException(); 
} 

于是两人的类型参数方法似乎是相关的。但我不明白如何。另外,(Call<T>)投似乎并不像一个很好的解决我的问题 - service是这样的改造接口:

public interface AuthServiceInterface { 
    @POST("auth/a") Call<AuthResponseA> authenticate(CredentialsA credentials); 
    @POST("auth/b") Call<AuthResponseB> authenticate(CredentialsB credentials); 
} 
+0

简单的答案 - Java不会resove它,如果你删除它,并用AuthResponse替换T - 什么都不会改变 –

回答

3

编译器能够推断类型参数T基于目标变量返回类型<T extends A>。例如,编译器可以理解TString,当你写

List<String> empty = Collections.emptyList(); 

在你的榜样,类型是<T extends AuthResponse>在两侧。


你应该明白,类型T在两种方法可能会有所不同:

private Call<T extends AuthResponse> getAuthCall(Credentials credentials) { 
    return (Call<T>) new BadAuthCall<BadResponse>(); // unchecked warning 
} 

public <T extends AuthResponse> void authenticate(Credentials credentials) { 
    Call<T> call = getAuthCall(credentials); 
    ... 

// then somewhere in your code 
Call<GoodResponse> response = service.authenticate(credentials); 
// this code compiles, but will throw ClassCastException in runrime 

第一种方法给未检查警告,因为返回值被转换为Call<T>这可以在发送方任何东西。当我们将Call<BadResponse>转换为Call<GoodResponse>时,编译器试图防止我们发生这种情况。

P.S.不要担心,在您的示例中,演员阵容最有可能是安全的。


而最后一件事:因为T实际价值无法验证方法中已知的,则允许调用仅AuthResponse类的方法。严格地说,extends关键字在方法内部不起重要作用,它只影响赋值给变量的返回值。

1

当你打电话到一个通用的方法,并没有提供明确的类型见证时,“推论”的目的是找出是否至少存在一种类型,可以为T选择,这将使调用编译。 T可能有多种选择,在这种情况下,不需要关心选择哪一个 - 编译器不需要选择类型参数,因为它不会编译到无论如何字节码 - 它所需要的只是满足于至少有一个有效的选择,然后完成它的工作。

当在任何参数或返回类型中未使用类型参数时,则根据定义,T的类型选择不会影响该调用是否编译。这意味着编译器可以完全忽略类型参数,因为它与调用的类型检查无关。或者换句话说,编译器可以在T的范围内任意选择任何类型,例如,它可以选择AuthResponseYourSubclassOfAuthResponseMadeUpSubclassOfAuthResponseThatDoesntExistInYourCode。没关系。选择不会影响通话是否编译。

这个荒谬的点指出,一个泛型方法的类型参数没有出现在参数或结果类型中是完全没有意义的,并且没有任何可能的用途

为什么你的方法需要通用?如果您的泛型方法编译,那么这意味着我可以采取它,并用AuthResponse(或者甚至用我在代码中添加的新定义的UselessSubclassOfAuthResponse)替换所有出现的T,并且它也将被编译并且是完全有效的您的方法的非通用版本。考虑一下。如果我这样做,那么第一行将会显示Call<UselessSubclassOfAuthResponse> call = getAuthCall(credentials);。您的getAuthCall()方法是否返回Call<UselessSubclassOfAuthResponse>(或Call<WhateverTheCallerWantsWithoutKnowingWhatItIs>)?可能不会。如果CallResponse的类型参数可以在不影响代码有效性的情况下被任意修改,那么可以是1)它们没有被使用,也没有必要,或者2)你的代码基本上是类型不安全的。