답변:
속성을 정의하는 것과 같은 것은 없습니다.
속성은 초기화시 메모리에 예약 된 데이터 컨테이너이므로 선언 만 할 수 있습니다.
반면에 함수는 정의되지 않고 (형, 이름, 매개 변수) 선언 될 수 있으며 (함수 본문 누락), 따라서 추상이 될 수 있습니다.
"Abstract"는 선언되었지만 정의되지 않았 음을 나타낼 뿐이므로 사용하기 전에 정의해야합니다. 그렇지 않으면 쓸모 없게됩니다.
아니요, 컴파일러로이를 강제 할 방법이 없습니다. $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의 constructor를 만들어 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"속성을 정의하도록 강제합니다.
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();} }
오늘도 같은 질문을했는데 2 센트를 더하고 싶습니다.
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
나는 정의하지 않지만 getter $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
어떤 것을 방지하기 위해 스마트 사람 (6 개월에서 자신 일명)을 할 유혹
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의 솔루션도 마음에 듭니다.