这是一个我编写的实例代码的简化示例,所以如果它有点做作,我会道歉.我想要做的是从单个嵌套类型参数中有效地获取两个类型参数.我很确定这是不可能的,但我想我会试一试.
//Not legal java code
public class Foo<C extends Collection<T>> { //where T is another type parameter
private C coll;
public Foo(C coll) {
this.coll = coll;
}
public void add(T elem){
this.coll.add(elem);
}
//UPDATED TO ADD GETTER
/**
* I may need to retrieve the collection again, or pass it
* on to another function that needs the specific C type
*/
public C getColl(){
return coll;
}
}
...
List<String> strings = new ArrayList<String>();
Foo<List<String>> foo = new …Run Code Online (Sandbox Code Playgroud) 所以我读了Eric Lippert的'Constraints不是签名的一部分',现在我明白规范指定在重载解析后检查类型约束,但我仍然不清楚为什么必须如此.以下是Eric的例子:
static void Foo<T>(T t) where T : Reptile { }
static void Foo(Animal animal) { }
static void Main()
{
Foo(new Giraffe());
}
Run Code Online (Sandbox Code Playgroud)
这不会编译,因为重载解析:Foo(new Giraffe())推断Foo<Giraffe>是最好的重载匹配,但是类型约束失败并抛出编译时错误.用埃里克的话说:
这里的原则是重载决策(和方法类型推断)找到参数列表和每个候选方法的形式参数列表之间的最佳匹配.也就是说,他们会查看候选方法的签名.
类型约束不是签名的一部分,但为什么不能呢?在某些情况下,考虑类型约束是签名的一部分是个坏主意吗?实施起来困难还是不可能?我并不是在提倡如果最佳选择的超载是出于无论什么原因无法调用的话,那么就会默默地回归到第二好的; 我讨厌那个.我只是想了解为什么不能使用类型约束来影响最佳过载的选择.
我想象在C#编译器内部,仅用于重载解析(它不会永久重写方法),如下所示:
static void Foo<T>(T t) where T : Reptile { }
Run Code Online (Sandbox Code Playgroud)
变成了:
static void Foo(Reptile t) { }
Run Code Online (Sandbox Code Playgroud)
为什么不能将类型约束"拉入"形式参数列表?这怎么会以任何不好的方式改变签名?我觉得它只会加强签名.然后Foo<Reptile>永远不会被视为超载候选者.
编辑2:难怪我的问题太混乱了.我没有正确阅读Eric的博客,我引用了错误的例子.我在示例中编辑了我觉得更合适.我还将标题更改为更具体.这个问题似乎并不像我最初想象的那么简单,也许我错过了一些重要的概念.我不太确定这是stackoverflow材料,这个问题/讨论可能最好转移到其他地方.
c# generics type-constraints overload-resolution nested-generics
首先,感谢您的光临.我正在玩Swift 3.1嵌套泛型,我在初始化时遇到了错误.
class NestedProduct<T> {
enum Gadget {
case smartphone
case laptop
case fridge
case others(T)
}
enum Company {
case Samsung
case Apple
case Sony
case others(T)
}
let company: Company
let gadget: Gadget
let reviews: [T]
init(enterCompany: Company, enterGadget: Gadget, enterReView: [T]) {
company = enterCompany
gadget = enterGadget
reviews = enterReView
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我尝试初始化
let product = NestedProduct<String>(enterCompany: NestedProduct.Company.Apple,
enterGadget: NestedProduct.Gadget.laptop,
enterReView: ["Good"])
Run Code Online (Sandbox Code Playgroud)
但是,我收到一条错误消息,
GenericCache(0x11102a518):检测到循环元数据依赖性,中止
我不知道为什么会这样.你能帮忙吗?谢谢!
class HasId<I> {}
class HasStringId extends HasId<String> {}
class Alert<T extends /*Some*/Object> extends HasStringId {}
class BaseController<M extends HasId<String>> {
// abstract Class<M> getModelClass();
}
class AlertController extends BaseController<Alert> { // error here
// @Override Class<Alert> getModelClass() {
// return Alert.class;
// }
}
Run Code Online (Sandbox Code Playgroud)
在OpenJDK6上编译很好,但在OpenJDK7中给出:
AlertController.java:50: error: type argument Alert is not within bounds of
type-variable T
class AlertController extends BaseController<Alert> {
^
where T is a type-variable:
T extends HasId<String> declared in class BaseController
Run Code Online (Sandbox Code Playgroud)
请注意,第50行有rawtype警告,因为必须参数化Alert.如果我这样做,例如extends BaseController<Alert<Object>>代码编译.但我不能这样做,因为我需要实现getModelClass().
更新:这是Java …
我试图弄清楚如何让泛型跳过篮球.
我有:
interface Root { }
interface Middle extends Root { }
class Type implements Root { }
Run Code Online (Sandbox Code Playgroud)
还有很多"Subtype"类:
class Subtype1 extends Type implements Middle { }
class Subtype2 extends Type implements Middle { }
...
Run Code Online (Sandbox Code Playgroud)
我要的是声明一个类有两个类型参数T和S,在T被束缚Type和S被束缚T和Middle.
我无法看到泛型的方法来确保S扩展T和实现Middle.我想要的是:
class Handler<T extends Root, S extends T, S extends Middle>;
Run Code Online (Sandbox Code Playgroud)
要么
class Handler<T extends Root, S extends <T extends Middle>>;
Run Code Online (Sandbox Code Playgroud)
但当然两者都不合法.也许有一些我不知道的魔法?
TL;博士
尝试实现一个层次流畅的界面,这样我就可以组合节点子类,同时也可以独立于类,但是获取类型参数不在其绑定错误范围内.
细节
我正在尝试实现一个解决方案,以便我可以创建一些类似于以下内容的东西:
farm
.animal()
.cat()
.meow()
.findsHuman()
.saysHello()
.done()
.done()
.dog()
.bark()
.chacesCar()
.findsHuman()
.saysHello()
.done()
.done()
.done()
.human()
.saysHello()
.done();
Run Code Online (Sandbox Code Playgroud)
同时也能做到:
Human human = new Human()
.saysHello()
Run Code Online (Sandbox Code Playgroud)
我已经接近使用各种策略,但未能获得所描述的灵活性.
我当前的尝试使用以下类:
abstract class Base<T extends Base<T>>{
private T parent;
Base(){
}
Base( T parent ){
this.parent = parent;
}
public T done() throws NullPointerException{
if ( parent != null ){
return (T) parent;
}
throw new NullPointerException();
}
}
class Farm<T extends Base<T>> extends Base{
private Animal<Farm<T>> …Run Code Online (Sandbox Code Playgroud) 我正在对一些高度声明性的代码进行原型设计,而Kotlin附带的类型推断和安全性有很大帮助.其中一个目标是使主要类型的扩展(子类)易于实现.为了保持丰富的类型推理和表达能力,我发现在定义针对子类投影的泛型扩展函数方面取得了一些成功.子类方法的所有类型信息都没有额外的子类实现,这很好.
所以我正在尝试编写一个丰富的通用函数来维护尽可能多的类型信息.问题随着这个函数在潜在的递归泛型类型上运行这一事实而兴起,我想要改变泛型类型参数.
没有例子,这是不可能描述的.所以考虑:
open class G<in T>
class A<in T> : G<T>()
class B<in T> : G<T>()
class C<in T> : G<T>()
val ba = B<A<*>>()
val cb = C<B<*>>()
Run Code Online (Sandbox Code Playgroud)
我们想要一个能够有效地执行此操作的功能,除了一般情况
fun B<A<*>>.doTransitiveThing(c: C<B<*>>) : C<A<*>>
{
// implement
}
val ca = ba.doTransitiveThing(cb) // Returns C<A<*>>
Run Code Online (Sandbox Code Playgroud)
目标标准:
C作为PARAM并返回C,除了与不同一般类型参数G
G因此参数必须C<B<*>>代替C<G<*>>调用时B<A<*>> 这描述了问题的要点.我不确定这种语言是否能够支持我想要的东西.我不确定类型擦除是否是导致这种情况不可能的因素,但到目前为止我无法找到它(如果是这样的话,也许我可以使用帮助).
以下是关闭
fun <
TargetGenericType,
Arg1Type: G<*>,
ReceiverType: G<TargetGenericType>,
Arg2Type: G<Arg1Type>,
ResultType: G<TargetGenericType>
>
ReceiverType.doTransitiveThingGeneric(x: …Run Code Online (Sandbox Code Playgroud) 请考虑以下尝试使用函数类型参数定义高阶类型函数的伪代码M<?>:
type HigherOrderTypeFn<T, M<?>> = T extends (...)
? M<T>
: never;
Run Code Online (Sandbox Code Playgroud)
M<?>是语法错误的 TypeScript,但将类型签名声明为会在第二行HigherOrderTypeFn<T, M>产生错误Type 'M' is not generic. ts(2315)。
假设这种类型目前在 TS 中无法表示,我是否正确?
可能重复:
没有使用通用扩展方法的类型推断
我有一个通用接口,以及它的两个具体实现.
然后我有一个方法,它是实现该接口的对象集合的扩展方法,它应该处理集合并返回已处理的集合.
处理操作不会更改对象也不会生成新对象,因此我希望该方法的输出与输入的类型相同.
换句话说,我不希望方法的输出是接口类型的集合,而是我传入的实际具体类型.
但是,这会在调用方法时导致类型推断出现问题.
我将用LINQPad示例说明:
void Main()
{
var intervals = new Interval<int>[]
{
new Interval<int>(),
new Interval<int>(),
};
var ordered1 = intervals.Process(); // error 1
// var ordered2 = Extensions.Process(intervals); // error 2
}
public static class Extensions
{
public static IEnumerable<TInterval> Process<TInterval, T>(
this IEnumerable<TInterval> intervals)
where TInterval : IInterval<T>
{
return intervals;
}
}
public interface IInterval<T> { }
public class Interval<T> : IInterval<T> { }
Run Code Online (Sandbox Code Playgroud)
这给了我以下两个编译时错误,你必须注释掉并在相关的行中激发两条消息:
错误1:
'Interval <int> []'不包含'Process'的定义,并且没有扩展方法'Process'接受类型'Interval <int> …
我有以下代码:
IEnumerable<IList<MyClass>> myData = //...getMyData
foreach (MyClass o in myData)
{
// do something
}
Run Code Online (Sandbox Code Playgroud)
它编译,运行,显然我得到了System.InvalidCastException.
为什么编译器不抱怨?MyClass是一个简单的bean,没有扩展名.
编辑1:
正如David所建议的那样,将类型IList转换List为编译器抱怨
编辑2:
我已经明白行为是在C#语言定义中指定的.但是,我不明白为什么允许这样的转换/转换,因为在运行时我总是得到一个InvalidCastException.为了更深入,我打开了这个.
generics ×10
nested-generics ×10
java ×4
c# ×3
kotlin ×1
openjdk ×1
swift ×1
swift3.1 ×1
typescript ×1