在从构造函数或init块中调用的重写抽象函数中初始化variables时,variables未正确初始化

我碰到了一些Kotlin代码的问题,我发现它涉及到调用一个从init块(或二级构造函数,或者重现这个问题)中分配一些variables的方法。

MCVE:

abstract class Shader(/*Input arguments omitted for the sake of an MCVE*/){ init{ //Shader loading and attaching, not relevant bindAttribs()//One of the abstract methods. In my actual program, this uses OpenGL to bind attributes //GLSL program validation getUniforms()//Same as the previous one: abstract method using GL calls to get uniforms. This gets locations so an integer is set (the problem) } abstract fun getUniforms();//This is the one causing problems abstract fun bindAttribs();//This would to if primitives or non-lateinit vars are set } abstract class BoilerplateShader() : Shader(){ var loc_projectionMatrix: Int = 404//404 is an initial value. This can be anything though var loc_transformationMatrix: Int = 404 var loc_viewMatrix: Int = 404 override fun getUniforms(){ //These would be grabbed by using glGetUniformLocations, but it's reproducable with static values as well loc_projectionMatrix = 0 loc_transformationMatrix = 1 loc_viewMatrix = 2 println(loc_projectionMatrix.toString() + ", " + loc_transformationMatrix + ", " + loc_viewMatrix) } //debug method, only used to show the values fun dump(){ println(loc_projectionMatrix.toString() + ", " + loc_transformationMatrix + ", " + loc_viewMatrix) } } class TextureShader() : BoilerplateShader(){ override fun bindAttribs() { //This doesn't cause a problem even though it's called from the init block, as nothing is assigned //bindAttrib(0, "a_position"); //bindAttrib(1, "a_texCoord0"); } } //Other repetitive shaders, omitted for brevity 

然后做:

 val tx = TextureShader() tx.dump() 

打印:

 0, 1, 2 404, 404, 404 

打印语句按照从getUniforms到最后的转储调用的顺序调用。 它在getUniforms方法中分配的很好,但是在几毫秒之后调用它们时,它们突然被设置为默认值(在本例中)为404.这个值可以是任何东西,但是我使用404,因为这是一个值我知道我不会用这个特定的MCVE进行测试。

我使用的是一个严重依赖抽象类的系统,但是调用其中一些方法( getUniforms非常重要)是必须的。 如果我通过调用getUniformsBoilerplateShader或者TextureShader添加一个init块,它可以正常工作。 在创建对象之后调用一个init函数(不是init块)

 fun init(){ bindAttribs(); getUniforms(); } 

工作正常。 但是,这将涉及创建的实例手动调用它:

 val ts = TexturedShader(); ts.init(); ts.dump() 

这不是一个选项。 在Java中编写导致Kotlin问题的代码可以像预期的那样工作(相当短的代码,但仍然可重现):

 abstract class Shader{ public Shader(){ getUniforms(); } public abstract void getUniforms(); } abstract class BoilerplateShader extends Shader{ int loc_projectionMatrix;//When this is initialized, it produces the same issue as Kotlin. But Java doesn't require the vars to be initialized when they're declared globally, so it doesn't cause a problem public void getUniforms(){ loc_projectionMatrix = 1; System.out.println(loc_projectionMatrix); } //and a dump method or any kind of basic print statement to print it after object creation } class TextureShader extends BoilerplateShader { public TextureShader(){ super(); } } 

如预期的那样,在variables和类的初始化打印0之后打印该variables的值。

试图用对象再现相同的东西, 当var不是延迟时,产生与数字相同的结果。 所以这:

 var test: String = "" 

打印:

 0, 1, 2, test 404, 404, 404, 

最后一行与打印完全一致:默认情况下,如果test的值设置为空字符串,则显示为空。

但是,如果var被声明为lateinit var

 lateinit var test: String 

它打印:

 0, 1, 2, test 404, 404, 404, test 

我不能用lateinit声明原语 。 而且由于它是在构造函数之外调用的,所以它需要被初始化或者被声明为lateinit

那么,是否有可能从重写的抽象方法初始化原语而不创建函数来调用它?


编辑:

一条评论提出了一个工厂方法,但由于抽象,这是不行的。 由于尝试的目标是调用基类( Shader )中的方法,并且由于抽象类不能被初始化,所以工厂方法在每个类中都不创建手动实现的情况下将不起作用,这是过度的。 如果构造函数是私有的,以避免工厂方法初始化,扩展将无法工作( is private in Shader )。

所以构造函数被强制公开(无论Shader类是否有主要构造函数或次要构造函数,子类必须有初始化才能初始化它),这意味着可以绕过工厂方法创建着色器。 并且,抽象再次引发问题,工厂方法(必须是抽象的)将在每个子类中手动实现,再次导致初始化并手动调用init()方法。

现在的问题仍然是,当从构造函数调用抽象方法时,是否可以确保非lateinit和primitives被初始化。 如果没有涉及抽象的话,创建工厂方法将是一个完美的解决方案。