我正在尝试在超类中实现某些功能,因此不必总是在其子级中重复该功能。样本:
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)
}
}
答案 0 :(得分:2)
Cat
扩展了Animal
,但Cat => Cat
未扩展Animal => Animal
。
A => B
相对于B
是协变的,相对于A
是互变的,即如果A1 <: A
,B1 <: B
则A => 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>