对子类型参数使用超级方法

时间:2019-04-25 17:32:08

标签: scala inheritance covariance contravariance subtyping

我正在尝试在超类中实现某些功能,因此不必总是在其子级中重复该功能。样本:

trait Animal {
  def applyF(transition: Animal => Animal): Animal = transition(this) // Animal as param and return type
}
case class Cat(color: String) extends Animal {
  def changeColor(color: String): Cat = this.copy(color)

  def update(): Animal = {
    val transition = (cat: Cat) => cat.changeColor("yellow") // Cat as param and return type
    applyF(transition) // <-- Type mismatch, expected: Animal => Animal, actual: Cat => Cat
  }
}

但这会导致类型不匹配,因为Cat不是Animal。为什么这不起作用? 猫扩展了动物,所以应该是动物吧?

这与协/反变量有关吗?

我该如何解决?

-----更新-----

第二个示例:

trait Animal {
  def applyF[A >: this.type <: Animal](transitions: Iterable[A => Animal]): Animal =
    transitions.foldLeft(this)((animal, transition) => transition(animal))
}
case class Cat(color: String) extends Animal {
  def changeColor(color: String): Cat = this.copy(color)

  def update(): Animal = {
    val transition = (cat: Cat) => cat.changeColor("yellow") // Cat as param and return type
    applyF(Iterable(transition)) // <-- Type mismatch, expected: A, actual: entity.type (with underlying type example.state.Entity)
  }
}

2 个答案:

答案 0 :(得分:2)

Cat扩展了Animal,但Cat => Cat未扩展Animal => Animal

A => B相对于B是协变的,相对于A是互变的,即如果A1 <: AB1 <: BA => B1 <: A => B <: A1 => B。 / p>

如果您将Animal#applyF参数化怎么办?

trait Animal {
  def applyF[A >: this.type <: Animal](transition: A => Animal): Animal = transition(this)
}

trait Animal { 
  def applyF[A >: this.type <: Animal](transitions: Iterable[A => A]): Animal /*A*/ =
    transitions.foldLeft[A](this)((animal, transition) => transition(animal)) 
}

答案 1 :(得分:1)

其他选择是使用F-Bounded Polymorphism

<script src="https://www.gstatic.com/firebasejs/5.10.0/firebase.js"></script>
<!-- Firebase App is always required and must be first -->
<script src="https://www.gstatic.com/firebasejs/5.8.4/firebase-app.js"></script>

<!-- Add additional services that you want to use -->
<script src="https://www.gstatic.com/firebasejs/5.8.4/firebase-auth.js"></script>
<script>
    // Initialize Firebase
    var config = {
        apiKey: "...",
        authDomain: "...",
        databaseURL: "...",
        projectId: "...",
        storageBucket: "...",
        messagingSenderId: "..."
    };
    firebase.initializeApp(config);
</script>

<script>
    function googleLogin(){
        var provider = new firebase.auth.GoogleAuthProvider();
        firebase.auth().useDeviceLanguage();
        firebase.auth().signInWithPopup(provider).then(function(result) {
            // This gives you a Google Access Token. You can use it to access the Google API.
            var token = result.credential.accessToken;
            // The signed-in user info.
            var user = result.user;
            // ...
            firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
                var csrftoken = '{{ csrf_token }}';
                $.post("/api/login", {'csrfmiddlewaretoken': csrftoken, 'token': idToken});
                document.location.reload(true);
            }).catch(function(error) {
                alert("Error al iniciar sesión")
                // Handle error
            });
            }).catch(function(error) {
            // Handle Errors here.
            var errorCode = error.code;
            var errorMessage = error.message;
            // The email of the user's account used.
            var email = error.email;
            // The firebase.auth.AuthCredential type that was used.
            var credential = error.credential;
            // ...
        });
    }
</script>

但是,请记住it does bring its own problems