Answers:
没有定义属性的事情。
您只能声明属性,因为它们是初始化时保留在内存中的数据容器。
另一方面,可以在不定义函数的情况下声明函数(类型,名称,参数)(缺少函数体),因此可以将其抽象化。
“摘要”仅表示已声明但未定义的内容,因此在使用前,您需要先定义它,否则它将变得无用。
不,没有办法用编译器强制执行,您必须对$tablename
变量使用运行时检查(例如,在构造函数中),例如:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
要对Foo_Abstract的所有派生类强制执行此操作,必须制作Foo_Abstract的构造函数final
,以防止覆盖。
您可以声明一个抽象的getter:
abstract class Foo_Abstract {
abstract public function get_tablename();
}
class Foo extends Foo_Abstract {
protected $tablename = 'tablename';
public function get_tablename() {
return $this->tablename;
}
}
如果要在子对象中强制声明抽象对象属性,则取决于属性的上下文,我喜欢static
在抽象对象构造函数或setter / getter方法中使用带有关键字的常量作为属性。您可以选择使用final
来防止该方法在扩展类中被覆盖。
除此之外,子对象将覆盖父对象的属性和方法(如果重新定义)。例如,如果protected
在父级中声明一个属性,在子级中声明一个属性public
,则结果属性是公共的。但是,如果private
在父级中声明了该属性,则该属性将保留private
并且对子级不可用。
http://www.php.net//manual/en/language.oop5.static.php
abstract class AbstractFoo
{
public $bar;
final public function __construct()
{
$this->bar = static::BAR;
}
}
class Foo extends AbstractFoo
{
//const BAR = 'foobar';
}
$foo = new Foo; //Fatal Error: Undefined class constant 'BAR' (uncomment const BAR = 'foobar';)
echo $foo->bar;
如上所述,没有这样的确切定义。但是,我使用这种简单的解决方法来强制子类定义“抽象”属性:
abstract class Father
{
public $name;
abstract protected function setName(); // now every child class must declare this
// function and thus declare the property
public function __construct()
{
$this->setName();
}
}
class Son extends Father
{
protected function setName()
{
$this->name = "son";
}
function __construct(){
parent::__construct();
}
}
static
属性问题。
the only "safe" methods to have in a constructor are private and/or final ones
,我的解决方法不是这种情况吗?我在里面使用私人
$name
。您可以在setName()
没有实际设置的情况下实现该功能$name
。
getName
代替$name
效果更好。abstract class Father { abstract protected function getName(); public function foo(){ echo $this->getName();} }
我今天问过自己同样的问题,我想加两分钱。
我们想要abstract
属性的原因是要确保子类定义它们,并在未定义时抛出异常。在我的特定情况下,我需要一些可以与static
盟友一起使用的东西。
理想情况下,我想要这样的东西:
abstract class A {
abstract protected static $prop;
}
class B extends A {
protected static $prop = 'B prop'; // $prop defined, B loads successfully
}
class C extends A {
// throws an exception when loading C for the first time because $prop
// is not defined.
}
我最终完成了该实施
abstract class A
{
// no $prop definition in A!
public static final function getProp()
{
return static::$prop;
}
}
class B extends A
{
protected static $prop = 'B prop';
}
class C extends A
{
}
如您所见,A
我没有定义$prop
,但是我在static
吸气剂中使用它。因此,以下代码有效
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
在C
,在另一方面,我并不确定$prop
,所以我得到的例外:
C::getProp();
// => Exception!
$c = new C();
$c->getProp();
// => Exception!
我必须调用该getProp()
方法来获取异常,并且在类加载时无法获取该异常,但至少在我看来,它与所需的行为非常接近。
我的定义getProp()
是final
为了避免某些聪明的人(六个月内又名我自己)被诱惑去做
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
对抽象属性的需求可以指示设计问题。虽然许多答案都实现了一种Template方法模式,并且可以正常工作,但它看起来总是很奇怪。
让我们看一下原始示例:
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
标记事物abstract
是表示它必须具备的功能。好吧,必须具有的值(在这种情况下)是必需的依赖项,因此应在实例化期间将其传递给构造函数:
class Table
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function name(): string
{
return $this->name;
}
}
然后,如果您实际上想要一个更具体的命名类,则可以像这样继承:
final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}
如果您使用DI容器并且必须为不同的对象传递不同的表,这将很有用。
PHP 7使制作抽象“属性”变得相当容易。就像上面一样,您将通过创建抽象函数来创建它们,但是使用PHP 7可以定义该函数的返回类型,这在构建任何人都可以扩展的基类时使事情变得容易得多。
<?php
abstract class FooBase {
abstract public function FooProp(): string;
abstract public function BarProp(): BarClass;
public function foo() {
return $this->FooProp();
}
public function bar() {
return $this->BarProp()->name();
}
}
class BarClass {
public function name() {
return 'Bar!';
}
}
class FooClass extends FooBase {
public function FooProp(): string {
return 'Foo!';
}
public function BarProp(): BarClass {
// This would not work:
// return 'not working';
// But this will!
return new BarClass();
}
}
$test = new FooClass();
echo $test->foo() . PHP_EOL;
echo $test->bar() . PHP_EOL;
如果tablename的值在对象的生命周期内永不改变,则以下将是一个简单而安全的实现。
abstract class Foo_Abstract {
abstract protected function getTablename();
public function showTableName()
{
echo 'my table name is '.$this->getTablename();
}
}
class Foo extends Foo_Abstract {
//Foo must 'implement' getTablename()
protected function getTablename()
{
return 'users';
}
}
这里的关键是指定字符串值'users'并直接在子类实现的getTablename()中返回。该函数模仿“只读”属性。
这与先前发布的使用附加变量的解决方案非常相似。我也喜欢Marco的解决方案,尽管它可能要复杂一些。