【问题标题】:What happened when I called a method from a subclass from the abstract superclass activity?当我从抽象超类活动的子类调用方法时发生了什么?
【发布时间】:2015-06-19 07:03:34
【问题描述】:

我想从我的超类中调用一个子类函数(它扩展了活动)。但是,我找不到子类的实例,所以我只是天真地尝试向下转换我的抽象超类并调用它的方法来看看会发生什么。我没想到这会起作用 - 超类怎么知道要在哪个实例上调用该方法?

public abstract class RootActivity extends Activity{
    private flag someCondition;

    @Override 
    protected void onCreate(Bundle savedInstanceState){ 
        //... 
    }
    // ... 

    public void startJob(JobAction.Id jobaction){
        Log.d("Zlatan", "started a job");
        if (!jobaction.someCondition){
            return;
        }else{
            ((SpecificJob) this).dontDelete(); //<--- What have I done?
            startSomeLongAsynchronousJob(someCondition);
            Log.d("Zlatan", "calling finish");
            finish();
        }

    }
    // ...
    public void someOtherFunction(){
        finish();
    }
    @Override 
    public boolean onKeyDown(int keyCode, KeyEvent event){
        //...
        startJob(JobAction.SOMEENUM);  //Startjob is being called in the superclass
    }
}
}

我在这里有一个子类。该活动是由另一个活动发起的,我在这里也包含了清单 sn-p。

public class SpecificJob extends SomeClassThatExtendsRootActivity{
    //SomeClassThatExtendsRootActivity extends RootActivity - I've ommitted it
    private boolean flag = false;
    @Override
    public void onCreate(Bundle bundle){
        super.onCreate(bundle);
        //...
    }
    // ... some code ...
    public void dontDelete(){ //Not static
        flag = true;
        Log.d("Zlatan", "I set flag"); //This doesn't appear in log
    }

    @Override 
    public void onDestroy(){
        Log.d("Zlatan", "When finishing, flag is now " + flag);
    }
}

<activity
        android:name=".SpecificJob"
        android:label="@string/TXT_BOOKMARKS"
        android:icon="@drawable/ic_launcher"
        android:theme="@style/stuff"
        android:launchMode="singleTask"
        android:windowSoftInputMode="stateHidden"
        android:taskAffinity=".SpecificJob">
        <intent-filter>
            <action android:name="com.me.feature.SOME_WORKFLOW" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
        <meta-data
            android:name="concurrency"
            android:value="DEFAULT_CONCURRENCES|printer"
            />
    </activity>

令我惊讶的是,没有错误或崩溃。我的结果是

D/Zlatan started a job
D/Zlatan calling finish
D/Zlatan﹕ When finishing, flag is now false

我没有看到我在 SpecificJob.dontDelete() 中编写的日志。我的问题是

1) 这在运行时做了什么?为什么我没有看到“I set flag”,为什么没有崩溃?

2) 我怎样才能最好地从 RootActivity 调用 SpecificJob 中的函数?

【问题讨论】:

  • 你在哪里调用 startJob ?
  • startJob 在 RootActivity 的多个地方被调用,在 onOptionsItemSelected 和 onKeyDown 等重写函数中。这是我的问题的一部分;我知道我应该在 SpecificJob 类中拦截这些,但我有很多。

标签: java android inheritance casting


【解决方案1】:

直到运行时才检查强制转换,并且当您从 RootActivity 继承 SpecificJob 并且仅在 SpecificJob 上调用 startJob() 时,生成的对象仍然是 SpecificJob 类型。这意味着可以毫无问题地制作演员表。如果您要扩展 RootActivity 并尝试在这个新类上调用 startJob() 它将不起作用。

您不应该从超类调用子类。超类不应该知道它是如何扩展的。可能有更好的方法,你为什么要这样做?

更新:因此,将 RootActivity 和任何继承它的类使用的所有清理代码放入 RootActivity 的 onDestroy 方法中。

public abstract class RootActivity extends Activity{
    @Override 
    public void onDestroy(){
        // All cleanup code common to inherited classes goes here
    }
}

然后在 SpecificJob 中调用 super.onDestroy,然后调用 SpecificJob 独有的任何代码。

public class SpecificJob extends SomeClassThatExtendsRootActivity{
    @Override 
    public void onDestroy(){
        super.onDestroy();
        // All cleanup code unique to this class goes here
    }    

}

【讨论】:

  • 我在 SpecificJob 的 onDestroy 中做了一些清理工作。但是,我的清理代码中的条件之一取决于活动关闭是由 RootActivity.startJob(...) 中使用的完成还是通过其他方式造成的。我的解决方案是 1)在 RootActivity 中设置一个标志并在我不想这样做的 SpecificJob 中获取它,或者 2)拦截对 startJob 的每次调用,这只是很多代码,或者 3)在 RootActivity 中调用一个函数提醒 specificJob,这是我在这里尝试过的。
  • @EricS。好吧,如果清理取决于使用哪个类(例如 RootActivity/SpecificJob/SomeClassThatExtendsRootActivity),那么您应该分别将代码放入每个类中,然后调用超类的方法。我会更新我的答案。
  • 谢谢@mbdavis。我的清理代码实际上是在其他地方发送广播,而不是实际“清理”——我想这是用词不当。鉴于我正在查看的庞然大物,它可能无法正常工作,但无论如何谢谢。
  • @EricS。广播是如何产生的?这仍然是正确使用继承的途径 - 但是可能需要重新考虑
【解决方案2】:

正如已经提到的,这是因为对象“知道”它们的类型和类型转换仅在运行时检查。因此,如果在 SpecificJob 实例上调用 startJob() 它将起作用。但是,如果在另一个 RootActivity 子类的实例上调用该方法,您将在运行时收到异常。这也是您应该尽可能避免类型转换的原因。

在这种特殊情况下,很容易以类型安全的方式编写代码。只需将dontDelete() 方法的声明添加到RootActivity,就像这样

protected abstract void dontDelete();

这会强制具体的子类实现该方法并使类型转换变得不必要。

【讨论】:

  • 我很困惑它使用了哪个SpecificJob 实例,以及为什么SpecificJob 中的行没有运行。我没有指定实例,我认为可能发生了一些神奇的事情,因为 SpecificJob 是作为单任务活动启动的。它是否使用了抽象 RootActivity 的实例?
  • 每个SpecificJob 实例也是一个RootActivity 实例。这就是继承的工作原理:)
猜你喜欢
  • 2021-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-14
  • 2018-08-22
  • 2013-05-01
相关资源
最近更新 更多