Конструкция
class Rectangle(val topLeft: Point, val bottomRight: Point)
подразумевает под собой, что для класса сгенирируется конструктор с параметрами topLeft, bottomRight, в классе будут созданы соответствующие поля, и для этих полей сгенерируются геттеры, с теми же названиями.
В случае когда мы обращаемся к полю по сути мы обращаемся всё равно к геттеру. И в данном случае этот геттер будет иметь соответствующее описание, подходящее под
def topLeft: Point
.
Соответственно никаких противоречей не возникает, у нас есть геттер, который подходит под интерфейс класса от которого мы наследуемся. Если же мы объявим класс, например, как:
class Rectangle(val topLeft: Int, val bottomRight: Point) extends Rectangular
То программа не скомпилируется, соответственно.
Если углубиться глубже то на самом деле скала старается на различать val и def. Это оба равноправные объявления. Отличие между ними заключается в стратегии исполнения. Подробнее:
https://class.coursera.org/progfun-005/lecture/4
https://class.coursera.org/progfun-005/lecture/5