(我使用的是Eclipse Luna 4.4.0,JDK 1.8.0_05)
我制作游戏,游戏世界的拓扑结构大致可以分解为World -> Level -> Tile
,其中Tile是一小块地形单位。我设置了三个项目,一个包含这些结构的一些基类,另外两个是服务器和客户端,它们扩展了基础项目中的结构,以满足每个项目所需的其他内容。像这样:
基础项目:
public class BaseWorld{/* ...code... */}
public class BaseLevel{/* ...code... */}
public class BaseTile{/* ...code... */}
在服务器和客户端项目中:
public class World extends BaseWorld{/* ...extended code... */}
public class Level extends BaseLevel{/* ...extended code... */}
public class Tile extends BaseTile{/* ...extended code... */}
现在在基础项目的许多部分中,有一些方法可以返回基类,但在服务器和客户端项目中,这通常意味着我必须重写方法并转换为子类型,因为子项目专门使用子类型。像这样:
public class BaseLevel{
BaseLevel getNextLevel(){/* ...code... */}
}
public class Level extends BaseLevel{
Level getNextLevel(){
return (Level)super.getNextLevel();
}
}
这是一种难以维持的痛苦。我发现我可以使用泛型来解决其中的一些问题,具有以下模式:
public class BaseLevel<Level extends BaseLevel<Level>>{
Level getNextLevel(){/* ...code... */}
}
public class Level extends BaseLevel<Level>{
//All sorted! getNextLevel() already returns this subclass type.
}
嗯,我有这个疯狂的想法将上面的模式弯曲到断点,并且能够在所有超类中使用所有子类。现在我必须说我绝不赞同以下代码作为良好的编程,我只是有了这个想法并尽可能地把它拿出来看看它是如何工作的。无论如何,我想出了以下怪物。
public class BaseWorld<Tile extends BaseTile<Tile, Level, World>
,Level extends BaseLevel<Tile, Level, World>
,World extends BaseWorld<Tile, Level, World>>{
/* ...code... */
}
public class BaseLevel<Tile extends BaseTile<Tile, Level, World>
,Level extends BaseLevel<Tile, Level, World>
,World extends BaseWorld<Tile, Level, World>>{
/* ...code... */
}
public class BaseTile<Tile extends BaseTile<Tile, Level, World>
,Level extends BaseLevel<Tile, Level, World>
,World extends BaseWorld<Tile, Level, World>>{
/* ...code... */
}
现在,每当我需要从任何其他类中返回任何类时,我都可以在子项目中不必使用任何类型的任何类型!
很遗憾没有,因为当Eclipse重建工作区时:
A stack overflow error has occurred.
You are recommended to exit the workbench. [...]
我想当一个类的泛型正在被解决时,它必须去解析另一个类的泛型,然后陷入递归循环。
提问时间:
这个错误是Eclipse编辑器有中风还是java编译器?
上面没有在编辑器中突出显示为编译器警告/错误 - 它是否应该,即使它看起来在技术上有效?
这种递归是无限深度还是非常深?
是否有可能在避免此错误的同时实现我在此尝试的功能? (我再次强调它绝对不是一个好的模式,我只是想知道它是否可以工作)
答案 0 :(得分:4)
Eclipse编辑器是中风还是java编译器?
本例中的Eclipse编辑器。以下声明(当然是在不同的文件中)是有效的Java代码。
public class BaseFoo<F extends BaseFoo<F,B>, B extends BaseBar<F,B>> {}
public class BaseBar<F extends BaseFoo<F,B> ,B extends BaseBar<F,B>> {}
public class Foo<F extends BaseFoo<F,B>,B extends BaseBar<F,B>> extends BaseFoo<F,B> {}
public class Bar<F extends BaseFoo<F,B>,B extends BaseBar<F,B>> extends BaseBar<F,B> {}
以上并没有在编辑器中突出显示为编译器警告/错误 - 是否应该,即使它看起来在技术上有效?
它可能应该(好吧,好吧,好吧,我在这里开玩笑......)不。这是有效的Java。只是......呃......
这种递归可能是无限深,还是只是非常深?
无限,我在这里猜测(稍等一下我的猜测)。
考虑尝试实例化Foo
的实例。
继续,使用默认构造函数写出初始化程序。
Foo<Foo, Bar> foo = new Foo<Foo, Bar>();
这里有四个错误,关于通用边界不匹配。
让我们尝试解决它。
Foo<Foo<Foo,Bar>, Bar<Foo,Bar>> foo = new Foo<Foo<Foo,Bar>, Bar<Foo,Bar>>();
现在有12个错误。我们可以继续尝试初始化它。
是否有可能在避免此错误的同时实现我在此尝试的功能? (我再次强调它绝对不是一个好的模式,我只是想知道它是否可行)
考虑以下基本结构:
class World {
SomeCollection<Level> levels;
}
class Level {
SomeCollection<Tile> tiles;
}
class Tile { }
为什么?
上述代码的基本结构更倾向于composition over inheritance,并且除了某些内置泛型集合类型外,不使用泛型。由此提供的初始化样式的一个很好的例子是静态构建器,最后编写类似这样的东西来初始化World
:
World gameWorld = new World.Builder().
.loadLevels(levelSource)
.loadTiles(tileset)
.build();
我希望这会有所帮助。
结束记录
不要继续。这是探索问题的可能方法的探索途径。不要再去这里了。
答案 1 :(得分:0)
多么美妙的怪物!
您的代码在Eclipse 4.4.1和javac 1.8.0_45中编译好,并且似乎可以正常工作。我认为它们在早期版本的Java 8中对类型检查中更精细的部分有一些问题;也许他们现在已经把它排除了。
与jdphenix的观点相反,我认为这种设计可能很有用。在静态类型控制和代码重用方面,我不知道他的建议设计如何具有你的任何优势。
示例:
public class ElegantMonstrosity {
public void m() {
SubWorld world = new SubWorld();
List<SubLevel> levels = world.getTiles();
SubLevel level = levels.get(0);
List<SubTile> tiles = level.getTiles();
SubLevel nextLevel = level.getNextLevel();
}
}
class BaseWorld<
Tile extends BaseTile<Tile, Level, World>,
Level extends BaseLevel<Tile, Level, World>,
World extends BaseWorld<Tile, Level, World>> {
List<Level> getTiles() { return null; }
}
class BaseLevel<
Tile extends BaseTile<Tile, Level, World>,
Level extends BaseLevel<Tile, Level, World>,
World extends BaseWorld<Tile, Level, World>> {
Level getNextLevel() { return null; }
List<Tile> getTiles() { return null; }
}
class BaseTile<
Tile extends BaseTile<Tile, Level, World>,
Level extends BaseLevel<Tile, Level, World>,
World extends BaseWorld<Tile, Level, World>> {
}
class SubTile extends BaseTile<SubTile, SubLevel, SubWorld> {}
class SubLevel extends BaseLevel<SubTile, SubLevel, SubWorld> {}
class SubWorld extends BaseWorld<SubTile, SubLevel, SubWorld> {}